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 NSObject
gösterildiği gibi , öğesinden UIView
devralan bir sınıf gibi türetilmiş bir alt sınıfın türetilmiş bir NSObject
kapsayı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ı
null
bağ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.AddSubView
hayatta 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 DataSource
UITableView
öğ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 this
atayabilirsinizDataSource
.
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 UITableView
iç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 UITableView
GetCell
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 UIScrollView
veya 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 UIImageView
bir 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 UIScrollView
iş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.