Xamarin. iOS performansı

Kötü uygulama performansı, kendisini pek çok şekilde sunar. Bu, bir uygulamanın yanıt vermemeye başlamasına neden olabilir, yavaş kaydırmaya neden olabilir ve pil ömrünü azaltabilir. Ancak, performansı iyileştirmek yalnızca verimli koddan daha fazlasını içerir. Kullanıcının uygulama performansı deneyimi de göz önünde bulundurulmalıdır. Örneğin, kullanıcının diğer etkinlikleri gerçekleştirmesini engellemeden yürütülen işlemlerin yürütülmesi, kullanıcının deneyimini iyileştirmenize yardımcı olabilir.

Bu belgede, Xamarin. iOS uygulamalarındaki performans ve bellek kullanımını artırmak için kullanılabilen teknikler açıklanmaktadır.

Not

Bu makaleyi okumadan önce, Xamarin Platformu kullanılarak oluşturulan uygulamaların bellek kullanımını ve performansını geliştirmek için platforma özgü olmayan tekniklerin tartışıldığı platformlar arası performansıokumanız gerekir.

Güçlü döngüsel başvurularından kaçının

Bazı durumlarda, nesnelerin bellek çöp toplayıcı tarafından geri kazanılmasını engelleyebilecek güçlü başvuru döngüleri oluşturmak mümkündür. Örneğin, ' den devralan bir sınıf gibi, ' den türetilmiş bir NSObjectUIViewNSObject kapsayıcıya eklenen ve Objective-C Aşağıdaki kod örneğinde gösterildiği gibi öğesinden kesin olarak başvurulan bir alt sınıfın durumunu göz önünde bulundurun:

class Container : UIView
{
    public void Poke ()
    {
    // Call this method to poke this object
    }
}

class MyView : UIView
{
    Container parent;
    public MyView (Container parent)
    {
        this.parent = parent;
    }

    void PokeParent ()
    {
        parent.Poke ();
    }
}

var container = new Container ();
container.AddSubview (new MyView (container));

Bu kod Container örneği oluşturduğunda, C# nesnesi bir nesneye güçlü bir başvuruya sahip olur Objective-C . Benzer şekilde, MyView örnek bir nesneye güçlü bir başvuruya de sahip olur Objective-C .

Buna ek olarak, çağrısı, container.AddSubview yönetilmeyen örnekteki başvuru sayısını arttıracaktır MyView . Bu durumda, Xamarin. iOS çalışma zamanı, yönetilen GCHandleMyView kodda bir başvuruyu tutacağından emin olmadığı için nesneyi yönetilen kodda canlı tutmak üzere bir örnek oluşturur. Yönetilen bir kod perspektifinden, MyView nesne AddSubview , çağrı için değil, geri kazanılır GCHandle .

Yönetilmeyen MyView nesne, GCHandleMyViewolarak bilinen yönetilen nesneye işaret eder. Yönetilen nesne, örneğe bir başvuru içerir Container . Bu durumda, Container örneği nesnesine yönetilen bir başvuruya sahip olur MyView .

Kapsanan bir nesnenin kapsayıcısının bağlantısını sakladığı koşullarda, döngüsel başvuruya yönelik birkaç seçenek mevcuttur:

  • Kapsayıcının bağlantısını ayarlayarak döngüyü el ile bölün null .
  • Kapsanan nesneyi kapsayıcıdan el ile kaldırın.
  • DisposeNesneler üzerinde çağrı yapın.
  • Döngüsel başvurunun, kapsayıcıya zayıf bir başvuru tutmanın önüne kaçının. Zayıf başvurular hakkında daha fazla bilgi için.

Zayıf başvuruları kullanma

Bir döngüyü önlemenin bir yolu, alt öğeden üst öğeye zayıf bir başvuru kullanmaktır. Örneğin, yukarıdaki kod şöyle yazılabilir:

class Container : UIView
{
    public void Poke ()
    {
        // Call this method to poke this object
    }
}

class MyView : UIView
{
    WeakReference<Container> weakParent;
    public MyView (Container parent)
    {
        this.weakParent = new WeakReference<Container> (parent);
    }

    void PokeParent ()
    {
        if (weakParent.TryGetTarget (out var parent))
            parent.Poke ();
    }
}

var container = new Container ();
container.AddSubview (new MyView (container));

Burada, kapsanan nesne üst canlı tutamaz. Ancak, üst öğe, ' a yapılan çağrı ile alt öğeyi canlı tutar container.AddSubView .

Bu durum, bir eş sınıfının uygulamayı içerdiği temsilciyi veya veri kaynağı modelini kullanan iOS API 'Lerinde de gerçekleşir; Örneğin,Delegate özelliği veyaDataSourceUITableViewsınıfında.

Yalnızca bir protokol uygulama için oluşturulan sınıflar söz konusu olduğunda (örneğin, IUITableViewDataSource bir alt sınıf oluşturmak yerine), sınıfı sınıfında uygulayabilir ve yöntemi geçersiz kılabilir ve DataSource özelliğini öğesine atayabilirsiniz this .

Zayıf öznitelik

Xamarin. iOS 11,10 , özniteliğini sunmuştur. Benzer şekilde WeakReference <T> , [Weak]WeakReference <T>bölmek için, hatta daha az kod ile de kullanılabilir.

Şunu kullanan aşağıdaki kodu göz önünde bulundurun WeakReference <T> :

public class MyFooDelegate : FooDelegate {
    WeakReference<MyViewController> controller;
    public MyFooDelegate (MyViewController ctrl) => controller = new WeakReference<MyViewController> (ctrl);
    public void CallDoSomething ()
    {
        MyViewController ctrl;
        if (controller.TryGetTarget (out ctrl)) {
            ctrl.DoSomething ();
        }
    }
}

Kullanılarak denk kod [Weak] çok daha kısa:

public class MyFooDelegate : FooDelegate {
    [Weak] MyViewController controller;
    public MyFooDelegate (MyViewController ctrl) => controller = ctrl;
    public void CallDoSomething () => controller.DoSomething ();
}

Aşağıda, [Weak][Weak] deseninin bağlamında başka bir örnek kullanılması verilmiştir:

public class MyViewController : UIViewController
{
    WKWebView webView;

    protected MyViewController (IntPtr handle) : base (handle) { }

    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();
        webView = new WKWebView (View.Bounds, new WKWebViewConfiguration ());
        webView.UIDelegate = new UIDelegate (this);
        View.AddSubview (webView);
    }
}

public class UIDelegate : WKUIDelegate
{
    [Weak] MyViewController controller;

    public UIDelegate (MyViewController ctrl) => controller = ctrl;

    public override void RunJavaScriptAlertPanel (WKWebView webView, string message, WKFrameInfo frame, Action completionHandler)
    {
        var msg = $"Hello from: {controller.Title}";
        var alertController = UIAlertController.Create (null, msg, UIAlertControllerStyle.Alert);
        alertController.AddAction (UIAlertAction.Create ("Ok", UIAlertActionStyle.Default, null));
        controller.PresentViewController (alertController, true, null);
        completionHandler ();
    }
}

Güçlü başvurularla nesneleri elden atma

Güçlü bir başvuru varsa ve bağımlılığı kaldırmak zor olursa, bir Dispose yöntemi üst işaretçiyi temizler.

Kapsayıcılar için, Dispose Aşağıdaki kod örneğinde gösterildiği gibi, içerilen nesneleri kaldırmak için yöntemini geçersiz kılın:

class MyContainer : UIView
{
    public override void Dispose ()
    {
        // Brute force, remove everything
        foreach (var view in Subviews)
        {
              view.RemoveFromSuperview ();
        }
        base.Dispose ();
    }
}

Üst öğesine güçlü başvuru tutan bir alt nesne için, uygulamadaki üst öğeye yönelik başvuruyu temizleyin Dispose :

class MyChild : UIView
{
    MyContainer container;
    public MyChild (MyContainer container)
    {
        this.container = container;
    }
    public override void Dispose ()
    {
        container = null;
    }
}

Güçlü başvuruları serbest bırakma hakkında daha fazla bilgi için bkz. IDisposable kaynaklarını yayınlama. Bu blog gönderisine de iyi bir tartışma vardır: Xamarin. iOS, çöp toplayıcı ve ben.

Daha fazla bilgi

Daha fazla bilgi için, bkz. sevgi Ile birlikte COA 'da döngülerle devam etmek Için kurallar , StackOverflow üzerinde MonoTouch GC 'de bir hata mu ve StackOverflow üzerinde refcount 1? içeren yönetilen nesneleri tek tek dokunamıyoruz .

Tablo görünümlerini iyileştirme

Kullanıcılar örnekler için Kesintisiz kaydırma ve hızlı yükleme süreleri bekler UITableView . Ancak, hücreler derin iç içe görünüm hiyerarşileri içerdiğinde veya hücreler karmaşık düzenler içerdiğinde, kaydırma performansı düşebilir. Ancak, performansın düşmesine engel olmak için kullanılabilen teknikler vardır UITableView :

  • Hücreleri yeniden kullanın. Daha fazla bilgi için bkz. hücreleri yeniden kullanma.
  • Alt görünüm sayısını azaltın.
  • Bir Web hizmetinden alınan önbellek hücresi içeriği.
  • Özdeş olmaları durumunda herhangi bir satırın yüksekliğini önbelleğe alma.
  • Hücreyi ve diğer tüm görünümleri, opak yapın.
  • Görüntü ölçeklendirmesinin ve degradeden kaçının.

Toplu olarak bu teknikler, UITableView örneklerin düzgün şekilde kaydırmasına yardımcı olabilir.

Hücreleri yeniden kullan

Bir içinde yüzlerce satır görüntülenirken UITableView , UITableViewCell ekranda yalnızca küçük bir kez görüntülenirken yüzlerce nesne oluşturmak için bellek atık olur. Bunun yerine, bu yeniden kullanılan hücrelere yüklenen içerik ile yalnızca ekranda görünür olan hücreler belleğe yüklenebilir. Bu, yüzlerce ek nesnenin örneklenmesini ve zamandan ve belleğin kaydedilmesini engeller.

Bu nedenle, bir hücre ekrandan kaybolduğunda, aşağıdaki kod örneğinde gösterildiği gibi, görünümü yeniden kullanım için bir sıraya yerleştirilebilir:

class MyTableSource : UITableViewSource
{
    public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
    {
        // iOS will create a cell automatically if one isn't available in the reuse pool
        var cell = (MyCell) tableView.DequeueReusableCell (MyCellId, indexPath);

        // Perform required cell actions
        return cell;
    }
}

Kullanıcı kaydırıldığında, UITableViewGetCell görüntülenecek yeni görünümler istemek için geçersiz kılmayı çağırır. Bu geçersiz kılma sonra DequeueReusableCell yöntemi çağırır ve bir hücre yeniden kullanım için kullanılabilir olduğunda döndürülür.

Daha fazla bilgi için bkz. veri içeren bir tablo doldurulmayayönelik hücre yeniden kullanımı .

Donuk görünümler kullanma

Tanımlanmış saydamlığı olmayan görünümlerin özellik kümesine sahip olduğundan emin olun Opaque . Bu, görünümlerin çizim sistemi tarafından en iyi şekilde işlenmesini sağlayacaktır. Bu, özellikle bir görünüm bir görünümü katıştırıldığında UIScrollView veya karmaşık bir animasyonun parçasıysa önemlidir. Aksi halde, çizim sistemi görünümleri diğer içerikle, performansı önemli ölçüde etkileyebilecek şekilde bileşik olacak.

FAT Xıbs 'ten kaçının

Xıbs büyük ölçüde film şeritleri tarafından değiştirilse de, Xıbs 'in hala kullanılabileceği bazı durumlar vardır. Bir XIB belleğe yüklendiğinde tüm içerikleri, tüm görüntüler dahil olmak üzere belleğe yüklenir. XıB, hemen kullanılmayan bir görünüm içeriyorsa, bellek harcanmakta olur. Bu nedenle, Xıbs kullanılırken, görünüm denetleyicisi başına yalnızca bir XıB olduğundan ve mümkünse görünüm denetleyicisinin Görünüm hiyerarşisini ayrı Xıbs olarak ayırın.

Görüntü kaynaklarını iyileştirme

Görüntüler, uygulamaların kullandığı en pahalı kaynakların bazılarıdır ve genellikle yüksek çözünürlükte yakalanır. Bu nedenle, içinde uygulamanın paketinden bir görüntü görüntülenirken UIImageView görüntünün aynı boyutta olduğundan emin olun UIImageView . Çalışma zamanında görüntülerin ölçeklendirilmesi, özellikle ' a katıştırılmışsa, pahalı bir işlem olabilir UIImageViewUIScrollView .

Daha fazla bilgi için bkz. platformlar arası performans kılavuzundaki görüntü kaynaklarını iyileştirme .

Cihazlarda test etme

Fiziksel bir cihazda uygulamayı mümkün olduğunca erken dağıtmaya ve test etmeye başlayın. Simülatörleri cihazların davranışlarını ve sınırlamalarını kusursuz bir şekilde eşleşmeyin ve bu nedenle gerçek dünyada bir cihaz senaryosunda mümkün olduğunca erken test edilmesi önemlidir.

Özellikle Benzetici, fiziksel bir cihazın bellek veya CPU kısıtlamalarına benzemez.

Animasyonları görüntü yenileme ile eşitler

Oyunlar oyun mantığını çalıştırmak ve ekranı güncelleştirmek için sıkı döngülere sahip olmaya eğilimlidir. Tipik kare hızları, saniye başına otuz ile 60 kare arasındadır. Bazı geliştiriciler, oyun simülasyonlarını ekranda olabildiğince çok kez güncelleştirmelerini ve bir saniyede 60 kareden kaçının.

Ancak, ekran sunucusu, saniye başına 60 kez bir üst sınıra sahip ekran güncelleştirmelerini gerçekleştirir. Bu nedenle, ekranı bu sınırdan daha hızlı güncelleştirme denemesi, ekran ve mikro ilerme 'ye yol açabilir. Ekran güncelleştirmelerinin görüntüleme güncelleştirmesiyle eşitlenmesi için kodu yapılandırmak en iyisidir. Bu, CoreAnimation.CADisplayLink bir saniyede 60 karede çalışan görselleştirme ve Oyunlar için uygun bir süreölçer olan sınıfı kullanılarak elde edilebilir.

Ana animasyon Saydamlıktan kaçının

Çekirdek animasyon saydamlığın önlanması, bit eşlem birleştirme performansını geliştirir. Genel olarak, mümkünse saydam katmanlardan ve bulanık kenarlıkların önüne kaçının.

Kod oluşturmaktan kaçının

System.Reflection.EmitİOS çekirdeği dinamik kod yürütmeyi önlediği için, veya System.Reflection.Emit ile dinamik olarak kod üretilmeden kaçınılmalıdır.

Özet

Bu makalede, Xamarin. iOS ile oluşturulan uygulamaların performansını arttırmaya yönelik teknikler açıklanmaktadır. Toplu olarak bu teknikler, bir CPU tarafından gerçekleştirilen iş miktarını ve bir uygulama tarafından tüketilen bellek miktarını önemli ölçüde azaltabilir.