Yeni oluşturulan nesneleri bir yöntemden döndürmek için otomatik serbest bırakma havuzları gerekir. Örneğin bu kod parçasını düşünün:
- (NSString *)messageOfTheDay {
return [[NSString alloc] initWithFormat:@"Hello %@!", self.username];
}
Yöntemde oluşturulan dize bir tane alıkoyacaktır. Şimdi bu alıkonma sayısını kim serbest bırakma ile dengeleyecek?
Yöntemin kendisi mi? Mümkün değil, oluşturulan nesneyi döndürmek zorundadır, bu yüzden geri dönmeden önce onu serbest bırakmamalıdır.
Yöntemin arayanı? Arayan, serbest bırakılması gereken bir nesneyi almayı beklemez, yöntem adı yeni bir nesnenin oluşturulduğu anlamına gelmez, yalnızca bir nesnenin döndürüldüğünü ve bu döndürülen nesnenin bir sürüm gerektiren yeni bir nesne olabileceğini söyler, ancak mevcut olmayan bir şey ol. Yöntemin döndürdüğü şey bazı iç durumlara bile bağlı olabilir, bu nedenle arayan, o nesneyi serbest bırakması gerekip gerekmediğini bilemez ve ilgilenmemesi gerekir.
Arayanın döndürülen tüm nesneleri kuralla her zaman serbest bırakmak zorunda kalması durumunda, yeni oluşturulmamış olan her nesnenin, bir yöntemden dönmeden önce her zaman korunması gerekir ve kapsam dışına çıktığında arayan tarafından serbest bırakılması gerekir. tekrar iade edilir. Arayan kişi döndürülen nesneyi her zaman serbest bırakmayacaksa, birçok durumda tutma sayılarını tamamen değiştirmekten kaçınabileceğinden, bu birçok durumda oldukça verimsiz olacaktır.
Bu yüzden otomatik kiralama havuzları var, bu yüzden ilk yöntem aslında
- (NSString *)messageOfTheDay {
NSString * res = [[NSString alloc] initWithFormat:@"Hello %@!", self.username];
return [res autorelease];
}
autorelease
Bir nesneyi çağırmak onu otomatik çalışma havuzuna ekler, ancak bu otomatik olarak serbest bırakma havuzuna bir nesne eklemek ne anlama gelir? Bu, sisteminize " Bu nesneyi benim için serbest bırakmanızı istiyorum, ama şimdi değil, şimdi değil; bir sürümle dengelenmesi gereken bir tutma sayısı var, aksi takdirde bellek sızacak ama bunu kendim yapamam şu anda, nesnenin mevcut kapsamımın ötesinde hayatta kalmasına ihtiyaç duyduğumdan ve arayanım da benim için yapamayacağından, bunun yapılması gerektiği hakkında hiçbir bilgisi yok.Bu yüzden onu havuzunuza ekleyin ve havuzu, benim için de nesnemi temizle. "
ARC ile derleyici, bir nesneyi ne zaman tutacağınıza, bir nesneyi ne zaman serbest bırakacağınıza ve ne zaman bir otomatik serbest bırakma havuzuna ekleyeceğinize karar verir, ancak yine de yeni oluşturulan nesneleri bellek sızıntısı olmadan yöntemlerden döndürmek için otomatik serbest bırakma havuzlarının varlığını gerektirir. Apple, oluşturulan kodda, çalışma zamanı sırasında bazen otomatik yayın havuzlarını ortadan kaldıracak bazı şık optimizasyonlar yaptı. Bu optimizasyonlar, hem çağıranın hem de çağıranın ARC kullanmasını gerektirir (ARC ve ARC olmayan karışımı karıştırmayı yasal ve resmi olarak desteklenir) ve eğer durum buysa, sadece çalışma zamanında bilinebilir.
Bu ARC Kodunu düşünün:
// Callee
- (SomeObject *)getSomeObject {
return [[SomeObject alloc] init];
}
// Caller
SomeObject * obj = [self getSomeObject];
[obj doStuff];
Sistemin oluşturduğu kod, aşağıdaki kod gibi davranabilir (ARC ve ARC olmayan kodu serbestçe karıştırmanıza izin veren güvenli sürümdür):
// Callee
- (SomeObject *)getSomeObject {
return [[[SomeObject alloc] init] autorelease];
}
// Caller
SomeObject * obj = [[self getSomeObject] retain];
[obj doStuff];
[obj release];
(Arayandaki tutma / bırakma işleminin sadece savunma amaçlı bir güvenlik koruması olduğunu, kesinlikle gerekli olmadığını, kodun onsuz mükemmel şekilde doğru olacağını unutmayın)
Ya da her ikisinin de çalışma zamanında ARC kullandığını tespit etmesi durumunda bu kod gibi davranabilir:
// Callee
- (SomeObject *)getSomeObject {
return [[SomeObject alloc] init];
}
// Caller
SomeObject * obj = [self getSomeObject];
[obj doStuff];
[obj release];
Gördüğünüz gibi, Apple atuorelease'i ortadan kaldırır, böylece havuz tahrip edildiğinde gecikmiş nesne salınımı ve güvenlik tutulur. Bunun nasıl mümkün olduğu ve perde arkasında neler olup bittiğiyle ilgili daha fazla bilgi edinmek için bu blog yayınına göz atın .
Şimdi asıl soruya: Biri neden kullanmalı @autoreleasepool
?
Çoğu geliştirici için, bugün bu yapıyı kodlarında kullanmak için tek bir neden kaldı ve bu da bellek ayak izini mümkün olan yerlerde küçük tutmak. Örneğin, bu döngüyü düşünün:
for (int i = 0; i < 1000000; i++) {
// ... code ...
TempObject * to = [TempObject tempObjectForData:...];
// ... do something with to ...
}
Her çağrının otomatik olarak döndürülen tempObjectForData
yeni bir çağrı oluşturabileceğini varsayın TempObject
. For-loop, mevcut otomatik yeniden başlatma havuzunda toplanan bu geçici nesnelerden bir milyonunu oluşturur ve yalnızca bu havuz yok edildiğinde, tüm geçici nesneler de yok edilir. Bu gerçekleşene kadar, bu geçici nesnelerin bir milyonu bellekte kalır.
Bunun yerine kodu şöyle yazarsanız:
for (int i = 0; i < 1000000; i++) @autoreleasepool {
// ... code ...
TempObject * to = [TempObject tempObjectForData:...];
// ... do something with to ...
}
Daha sonra for-loop her çalıştığında yeni bir havuz oluşturulur ve her loop yinelemesinin sonunda imha edilir. Bu şekilde, bir milyon kez çalışan döngüye rağmen, herhangi bir zamanda en fazla bir geçici nesne bellekte asılı kalır.
Geçmişte NSThread
, yalnızca ana iş parçacığının otomatik olarak bir Cocoa / UIKit uygulaması için otomatik yayın havuzu olduğu için, iş parçacıklarını yönetirken (örn. Kullanarak ) otomatik olarak çalışma havuzlarını kendiniz de yönetmek zorunda kaldınız. Yine de bu, bugün başlamak için muhtemelen iplikleri kullanmayacağınız için bugün oldukça eski. GCD'leri DispatchQueue
veya NSOperationQueue
'leri kullanırsınız ve bu ikisi de sizin için bir blok / görev çalıştırmadan önce oluşturulan ve onunla bir kez yok edildiğinde sizin için en üst düzey otomatik kiralama havuzunu yönetir.