Aracılığıyla paylaş


Xamarin.iOS performansı

Kötü uygulama performansı birçok şekilde kendini gösterir. 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 kod uygulamaktan fazlasını içerir. Kullanıcının uygulama performansı deneyimi de dikkate alınmalıdır. Örneğin, işlemlerin kullanıcının diğer etkinlikleri gerçekleştirmesini engellemeden yürütülmesini sağlamak, kullanıcının deneyimini geliştirmeye yardımcı olabilir.

Bu belgede, Xamarin.iOS uygulamalarında performansı ve bellek kullanımını geliştirmek için kullanılabilecek 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 açıklandığı Platformlar Arası Performans makalesini okumalısınız.

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

Bazı durumlarda nesnelerin belleklerinin çöp toplayıcı tarafından geri alınmasını engelleyebilecek güçlü başvuru döngüleri oluşturmak mümkündür. Örneğin, aşağıdaki kod örneğinde NSObjectgösterildiği gibi , öğesinden UIViewdevralan bir sınıf gibi türetilmiş bir alt sınıfın türetilmiş bir NSObjectkapsayıcıya eklendiği ve öğesinden Objective-Ckesinlikle başvurulduğunu düşünün:

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 örneği oluşturduğunda Container , C# nesnesinin bir nesneye güçlü bir Objective-C başvurusu olur. Benzer şekilde, MyView örneğin de bir nesneye güçlü bir Objective-C başvurusu olur.

Buna ek olarak, çağrısı container.AddSubview yönetilmeyen MyView örnekte başvuru sayısını artırır. Böyle bir durumda, Xamarin.iOS çalışma zamanı yönetilen koddaki nesneyi canlı tutmak MyView için bir GCHandle örnek oluşturur, çünkü yönetilen nesnelerin buna bir başvuru tutacağı garanti değildir. Yönetilen kod perspektifinden bakıldığında, MyView çağrısı için olmadığı için nesnesi geri AddSubview kazanılacaktır GCHandle.

Yönetilmeyen MyView nesne, güçlü bağlantı olarak bilinen yönetilen nesneye işaret eden bir nesneye sahip GCHandle olur. Yönetilen nesne örneğe bir başvuru Container içerir. Buna karşılık Container örnek, nesneye yönetilen bir başvuruya MyView sahip olur.

kapsanan bir nesnenin kapsayıcısının bağlantısını tuttuğu durumlarda döngüsel başvuruyla başa çıkmak için çeşitli seçenekler vardır:

  • Kapsayıcı nullbağlantısını olarak ayarlayarak döngüyü el ile kesme.
  • Kapsanan nesneyi kapsayıcıdan el ile kaldırın.
  • Nesneleri çağırın Dispose .
  • Kapsayıcıya zayıf bir başvuru tutmak için döngüsel başvurudan kaçının. Zayıf başvurular hakkında daha fazla bilgi için.

WeakReferences 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 öğeyi canlı tutmaz. Ancak, üst öğesi yapılan çağrı aracılığıyla alt öğeyi container.AddSubViewhayatta tutar.

Bu, eş sınıfın uygulamayı içerdiği temsilci veya veri kaynağı desenini kullanan iOS API'lerinde de gerçekleşir; örneğin, Delegate özelliği veya DataSourceUITableView öğesini seçin.

Yalnızca bir protokol uygulamak için oluşturulan sınıflar söz konusu olduğunda, örneğinIUITableViewDataSource, alt sınıf oluşturmak yerine, yalnızca arabirimini sınıfına uygulayabilir ve yöntemini geçersiz kılabilir ve özelliğini öğesine thisatayabilirsinizDataSource.

Zayıf öznitelik

Xamarin.iOS 11.10 özniteliğini [Weak] kullanıma sunar. gibiWeakReference <T>, [Weak] güçlü döngüsel başvuruları kesmek için kullanılabilir, ancak daha az kodla.

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

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ılan [Weak] eşdeğer kod çok daha kısadır:

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

Aşağıda, temsilci deseni bağlamında kullanmanın [Weak] başka bir örneği 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şvurulara sahip nesnelerin atılması

Güçlü bir başvuru varsa ve bağımlılığı kaldırmak zorsa, bir Dispose yöntemin üst işaretçiyi temizlemesini sağlayın.

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

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 başvuruyu Dispose temizleyin:

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

Güçlü başvurular yayınlama hakkında daha fazla bilgi için bkz . Release IDisposable Resources. Burada daha fazla çöp toplama bilgisi de vardır.

Daha Fazla Bilgi

Daha fazla bilgi için bkz. Cocoa With Love'ta Döngüleri Korumaktan Kaçınma Kuralları, StackOverflow'da MonoTouch GC'de bir hata mı ve StackOverflow'da MonoTouch GC yönetilen nesneleri neden refcount > 1 ile sonlandıramıyor? konusuna bakın.

Tablo görünümlerini iyileştirme

Kullanıcılar, örnekler için UITableView sorunsuz kaydırma ve hızlı yükleme süreleri bekler. Ancak, hücreler 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ı olumsuz etkilenebilir. Ancak, düşük UITableView performansı önlemek için kullanılabilecek teknikler vardır:

  • Hücreleri yeniden kullanma. 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 hücre içeriğini önbelleğe alın.
  • Aynı olmayan satırların yüksekliğini önbelleğe alın.
  • Hücreyi ve diğer görünümleri opak yapın.
  • Görüntü ölçeklendirme ve gradyanlardan kaçının.

Bu teknikler toplu olarak örneklerin sorunsuz bir şekilde kaydırılmasında yardımcı UITableView olabilir.

Hücreleri yeniden kullanma

bir UITableViewiçinde yüzlerce satır görüntülerken, aynı anda ekranda yalnızca az sayıda nesne görüntülendiğinde yüzlerce UITableViewCell nesne oluşturmak bellek kaybı olur. Bunun yerine, yalnızca ekranda görünen hücreler belleğe yüklenebilir ve içerik bu yeniden kullanılan hücrelere yüklenir. Bu, yüzlerce ek nesnenin örneklenmesini önleyerek zaman ve bellek tasarrufu sağlar.

Bu nedenle, aşağıdaki kod örneğinde gösterildiği gibi bir hücre ekrandan kaybolduğunda görünümü yeniden kullanmak üzere bir kuyruğa 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ırdıkça, yeni görünümlerin UITableViewGetCell görüntülenmesini istemek için geçersiz kılmayı çağırır. Bu geçersiz kılma yöntemi çağırır DequeueReusableCell ve yeniden kullanılabilir bir hücre varsa döndürülür.

Daha fazla bilgi için bkz. Verilerle Tablo Doldurmada Hücre Yeniden Kullanımı.

Opak görünümleri kullanma

Saydamlığı tanımlı olmayan görünümlerin özellik kümesi olduğundan Opaque emin olun. Bu, görünümlerin çizim sistemi tarafından en uygun şekilde işlenmesini sağlar. Bu, özellikle bir görünüm içine eklendiğinde UIScrollViewveya karmaşık bir animasyonun parçası olduğunda önemlidir. Aksi takdirde, çizim sistemi görünümleri diğer içerikle birleştirebilir ve bu da performansı önemli ölçüde etkileyebilir.

Şişman XIB'lerden kaçının

XIB'ler büyük ölçüde görsel taslaklarla değiştirilse de, XIB'lerin hala kullanılabildiği bazı durumlar vardır. Bir XIB belleğe yüklendiğinde, tüm görüntüler dahil olmak üzere tüm içeriği belleğe yüklenir. XIB hemen kullanılmayan bir görünüm içeriyorsa bellek boşa harcanıyor demektir. Bu nedenle, XIB'leri kullanırken görünüm denetleyicisi başına yalnızca bir XIB olduğundan emin olun ve mümkünse görünüm denetleyicisinin görünüm hiyerarşisini ayrı XIB'lere ayırın.

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

Görüntüler, uygulamaların kullandığı en pahalı kaynaklardan bazılarıdır ve genellikle yüksek çözünürlüklerde yakalanır. Bu nedenle, içinde uygulamanın paketinden UIImageViewbir görüntü görüntülerken görüntünün ve UIImageView aynı boyutta olduğundan emin olun. Çalışma zamanında görüntüleri ölçeklendirmek, özellikle de içine eklenmişse UIImageView pahalı bir UIScrollViewişlem olabilir.

Daha fazla bilgi için Platformlar Arası Performans kılavuzundaki Görüntü Kaynaklarını İyileştirme bölümüne bakın.

Cihazlarda test

Bir uygulamayı fiziksel bir cihazda mümkün olan en erken şekilde dağıtmaya ve test etme işlemine başlayın. Simülatörler cihazların davranışlarına ve sınırlamalarına tam olarak uymaz ve bu nedenle gerçek dünya cihaz senaryosunda mümkün olduğunca erken test etmek önemlidir.

Özellikle simülatör hiçbir şekilde fiziksel bir cihazın bellek veya CPU kısıtlamalarını simüle etmez.

Animasyonları görüntüleme yenilemesiyle eşitleme

Oyunlar, oyun mantığını çalıştırmak ve ekranı güncelleştirmek için sıkı döngülere sahip olma eğilimindedir. Tipik kare hızları saniyede otuz ile altmış kare arasında değişir. Bazı geliştiriciler, oyun simülasyonlarını ekranda yapılan güncelleştirmelerle birleştirerek ekranı saniyede mümkün olduğunca çok kez güncelleştirmeleri gerektiğini düşünüyor ve saniyede altmış karenin ötesine geçmek isteyebilir.

Ancak, görüntü sunucusu ekran güncelleştirmelerini saniyede altmış kez üst sınırda gerçekleştirir. Bu nedenle, ekranı bu sınırdan daha hızlı güncelleştirmeye çalışmak, ekran yırtılması ve mikro takılmaya neden olabilir. Ekran güncelleştirmelerinin görüntü güncelleştirmesi ile eşitlenmesi için kodu yapılandırmak en iyisidir. Bu, saniyede altmış karede çalışan görselleştirme ve oyunlar için uygun bir zamanlayıcı olan sınıfı kullanılarak CoreAnimation.CADisplayLink elde edilebilir.

Çekirdek Animasyon saydamlığından kaçının

Çekirdek animasyon saydamlığından kaçınmak bit eşlem oluşturma performansını artırır. Genel olarak, mümkünse saydam katmanlardan ve bulanık kenarlıklardan kaçının.

Kod oluşturmadan kaçının

iOS çekirdeği dinamik kod yürütmeyi önlediğinden veya Dinamik Dil Çalışma Zamanı ile System.Reflection.Emit dinamik olarak kod oluşturmaktan kaçınılmalıdır.

Özet

Bu makalede, Xamarin.iOS ile oluşturulan uygulamaların performansını artırmaya yönelik teknikler açıklanmış ve ele alınmaktadır. Bu teknikler toplu olarak bir CPU tarafından gerçekleştirilen çalışma miktarını ve bir uygulama tarafından kullanılan bellek miktarını büyük ölçüde azaltabilir.