ARC ile @autoreleasepool neden hala gereklidir?


191

Çoğunlukla ARC (Otomatik Referans Sayma) ile, Objective-C nesneleri ile bellek yönetimi hakkında hiç düşünmemize gerek yoktur. NSAutoreleasePoolArtık s oluşturmasına izin verilmiyor , ancak yeni bir sözdizimi var:

@autoreleasepool {
    
}

Sorum şu: Manuel olarak serbest bırakmam / otomatik yayınlamam gerektiğinde neden buna ihtiyacım olacak?


DÜZENLEME: Tüm cevaplardan ve yorumlardan ne aldığımı özetle:

Yeni Sözdizimi:

@autoreleasepool { … } için yeni bir sözdizimi

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

[pool drain];

Daha önemlisi:

  • ARC kullandığı autoreleaseyanı sıra release.
  • Bunun için bir otomatik serbest bırakma havuzuna ihtiyaç vardır.
  • ARC sizin için otomatik yayın havuzu oluşturmaz. Ancak:
    • Her Kakao uygulamasının ana iş parçasında zaten bir otomatik yayın havuzu var.
  • Kullanmak isteyebileceğiniz iki durum vardır @autoreleasepool:
    1. İkincil bir iş parçacığındaysanız ve otomatik serbest bırakma havuzu olmadığında, sızıntıları önlemek için kendinizinkini yapmalısınız myRunLoop(…) { @autoreleasepool { … } return success; }.
    2. @Mattjgalloway cevabında gösterildiği gibi daha yerel bir havuz oluşturmak istediğinizde.

1
Üçüncü bir durum daha var: UIKit veya NSFoundation ile ilgili olmayan bir şey geliştirdiğinizde. Komut satırı araçlarını kullanan bir şey
Garnik

Yanıtlar:


215

ARC, alıkoyma, sürüm ve otomatik yayınlardan kurtulmaz, sadece sizin için gerekli olanları ekler. Bu yüzden hala alıkoyma çağrıları var, hala serbest bırakma çağrıları var, yine de otomatik serbest bırakma çağrıları var ve hala otomatik serbest bırakma havuzları var.

Yeni Clang 3.0 derleyicisi ve ARC ile yaptıkları diğer değişikliklerden biri NSAutoReleasePool, @autoreleasepoolderleyici yönergesiyle değiştirilmeleridir . NSAutoReleasePoolher zaman biraz özel bir "nesne" idi ve bunu kullanarak bir nesneyi kullanma sözdizimi bir nesne ile karıştırılmayacak, böylece genellikle biraz daha basit olacaktı.

Temel olarak, ihtiyacınız var @autoreleasepoolçünkü endişelenecek hala otomatik serbest bırakma havuzları var. Sadece autoreleaseçağrı ekleme konusunda endişelenmenize gerek yok .

Otomatik yayın havuzu kullanımına bir örnek:

- (void)useALoadOfNumbers {
    for (int j = 0; j < 10000; ++j) {
        @autoreleasepool {
            for (int i = 0; i < 10000; ++i) {
                NSNumber *number = [NSNumber numberWithInt:(i+j)];
                NSLog(@"number = %p", number);
            }
        }
    }
}

Oldukça uyumlu bir örnek, elbette, ama @autoreleasepooldış- fordöngüsünün içinde olmasaydın, dış fordöngü etrafında her seferinde 10000 yerine 100000000 nesneyi daha sonra serbest bırakırsınız.

Güncelleme: Ayrıca bu cevaba bakınız - https://stackoverflow.com/a/7950636/1068248 - çünkü @autoreleasepoolARC ile hiçbir ilgisi yok.

Güncelleme: Burada olup bitenlerin içine bir göz attım ve bloguma yazdım . Oraya bir göz atarsanız, ARC'nin ne yaptığını ve yeni stilin @autoreleasepoolve bir kapsamı nasıl tanıttığını tam olarak nasıl koruduğunu, bültenleri ve otomatik yayınları gerekli kılmak için derleyici tarafından kullanıldığını göreceksiniz .


11
Kalıntılardan kurtulmaz. Onları sizin için ekler. Referans sayımı hala devam ediyor, sadece otomatik. Bu nedenle Otomatik Referans Sayımı :-D.
mattjgalloway

6
Peki neden @autoreleasepoolbenim için de eklemiyor? Otomatik olarak yayınlanan veya serbest bırakılanları kontrol etmiyorsam (ARC bunu benim için yapar), ne zaman otomatik yayın havuzu oluşturacağımı nasıl bilebilirim?
mk12

5
Ancak otomatik serbest bırakma havuzlarınızın nerede hareket edeceğini kontrol edebilirsiniz. Varsayılan olarak tüm uygulamanızın etrafına sarılmış bir tane var, ancak daha fazlasını isteyebilirsiniz.
mattjgalloway

5
İyi soru. Sadece "bilmek" lazım. Birini bir GC dilinde neden bir çöp toplayıcısına neden devam edip şimdi bir toplama döngüsü gerçekleştirebileceğine dair bir ipucu eklemeyi düşünün. Belki de temizlenmeye hazır bir ton nesne olduğunu biliyorsunuz, bir grup geçici nesneyi tahsis eden bir döngünüz var, bu yüzden "bilirsiniz" (veya Instruments size söyleyebilir) döngü içine bir yayın havuzu eklemenin bir İyi bir fikir.
Graham Perks

6
Döngü örneği, otomatik çalıştırma olmadan mükemmel bir şekilde çalışır: değişken kapsam dışına çıktığında her nesne yeniden yerleştirilir. Otomatik çalıştırma olmadan kodu çalıştırmak sabit miktarda bellek alır ve işaretçilerin yeniden kullanıldığını gösterir ve bir nesnenin dealloc'una bir kesme noktası koymak, objc_storeStrong çağrıldığında her döngüde bir kez çağrıldığını gösterir. Belki OSX burada aptalca bir şey yapar, ancak otomatik çalıştırma havuzu iOS'ta tamamen gereksizdir.
Glenn Maynard

16

@autoreleasepoolhiçbir şeyi otomatik olarak yayınlamaz. Bir otomatik yayın havuzu oluşturur, böylece bloğun sonuna ulaşıldığında, blok etkinken ARC tarafından otomatik olarak yayınlanan tüm nesnelere serbest bırakma mesajları gönderilir. Apple'ın Gelişmiş Bellek Yönetimi Programlama Kılavuzu bunu şöyle açıklıyor:

Otomatik kiralama havuz bloğunun sonunda, blok içinde bir otomatik kiralama mesajı alan nesnelere bir yayın mesajı gönderilir - bir nesne, blok içinde her otomatik yayın mesajı gönderildiğinde bir yayın mesajı alır.


1
Şart değil. Nesne bir releaseileti alır, ancak tutma sayısı> 1 ise nesne yeniden konumlandırılmaz.
andybons

@andybons: güncellendi; Teşekkürler. Bu ARC öncesi davranıştan bir değişiklik mi?
outis

Bu yanlış. ARC tarafından serbest bırakılan nesnelere, ARC tarafından serbest bırakıldıkları anda, otomatik yayın havuzu olsun veya olmasın bırakma iletileri gönderilir.
Glenn Maynard

7

İnsanlar ARC'yi bir çeşit çöp toplama veya benzeri için yanlış anlıyorlar. Gerçek şu ki Apple bir süre sonra insanlar Amaç-C'nin bellek yönetimi (bütün olduğunu fark etti (teşekkürler LLVM ve çınlama projelerine), yani retainsve releasesvb) tam olarak otomatize edilebilir derleme zamanında . Bu, kodu çalıştırmadan önce bile kodu okuyarak! :)

Bunu yapabilmek için tek bir koşul vardır: Kurallara uymamız GEREKİR , aksi takdirde derleyici işlemi derleme zamanında otomatikleştiremez. Yani, biz emin olmak için asla kuralları ihlal, biz açıkça yazma izin verilmez release, retainvb olanlar çağrılar otomatik derleyici tarafından bizim koduna enjekte edilir. Dolayısıyla içten hala var autoreleases, retain, releasevb Sadece, artık onlara yazmaya gerek yok edilir.

A ARC derleme zamanında otomatiktir, bu da çöp toplama gibi çalışma süresinden çok daha iyidir.

Biz hala var @autoreleasepool{...}çünkü sahip olmak kuralların hiçbirini bozmaz, biz ücretsiz oluşturmak / boşaltmak havuzu ihtiyaç duyduğumuz her zaman :).


1
ARC, JavaScript ve Java'da olduğu gibi GC'yi işaretleme ve süpürme değil referans sayma GC'dir, ancak kesinlikle çöp toplamadır. Bu şu soruyu ele almaz - "siz yapabilirsiniz", "neden yapmalısınız" sorusuna cevap vermez. Yapmamalısın.
Glenn Maynard

3

Çünkü derleyiciye, otomatik olarak yayımlanan nesnelerin kapsam dışına çıkmasının ne zaman güvenli olduğu hakkında ipuçları sağlamanız gerekir.


Bunu ne zaman yapmanız gerektiğine dair bir örnek verebilir misiniz?
mk12


Yani ARC'den önce, örneğin, OpenGL uygulamam için ikincil bir iş parçacığında çalışan bir CVDisplayLink vardı, ancak bir şey otomatik olarak serbest bırakmadığımı (veya bunu yapan kütüphaneleri kullanmadığımı) biliyordum çünkü çalışma döngüsünde bir otomatik yayın havuzu oluşturmadım. Bu, @autoreleasepoolARC'nin bir şeyi otomatik olarak yayınlamaya karar verip vermeyeceğini bilmediğim için şimdi eklemem gerektiği anlamına mı geliyor ?
mk12

@ Mk12 - Hayır. Her zaman ana çalıştırma döngüsünde her seferinde boşaltılan bir otomatik bırakma havuzunuz olacaktır. Yalnızca otomatik olarak yayınlanan nesnelerin, aksi takdirde çalıştırma döngüsünde bir sonraki seferde otomatik olarak silinmeden önce boşaltılmasını sağlamak istediğinizde bir tane eklemeniz gerekir.
mattjgalloway

2
@DougW - Derleyicinin gerçekte ne yaptığına bir göz attım ve burada blog yazdım - iphone.galloway.me.uk/2012/02/a-look-under-arcs-hood- –-episode-3 /. Umarım hem derleme zamanında hem de çalışma zamanında neler olduğunu açıklar.
mattjgalloway

2

Alıntı: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html :

Otomatik Yayın Havuz Blokları ve İplikleri

Bir Kakao uygulamasındaki her bir iplik kendi otomatik yayın havuzu blok yığınını korur. Yalnızca temel bir program yazıyorsanız veya bir iş parçacığını ayırırsanız, kendi otomatik yayın havuzu bloğunuzu oluşturmanız gerekir.

Uygulamanız veya iş parçacığınız uzun ömürlüdür ve potansiyel olarak çok sayıda otomatik olarak yayınlanmış nesne oluşturuyorsa, otomatik yayın havuzu blokları kullanmanız gerekir (ana iş parçacığında AppKit ve UIKit gibi); aksi takdirde, otomatik olarak yayınlanan nesneler birikir ve bellek alanınız artar. Ayrılmış iş parçacığınız Cocoa aramaları yapmazsa, otomatik serbest bırakma havuz bloğu kullanmanıza gerek yoktur.

Not: NSThread yerine POSIX iş parçacığı API'larını kullanarak ikincil iş parçacıkları oluşturursanız, Cocoa çoklu iş parçacığı modunda olmadığı sürece Cocoa'yı kullanamazsınız. Kakao, çoklu iş parçacığı moduna yalnızca ilk NSThread nesnesini ayırdıktan sonra girer. İkincil POSIX iş parçacıkları için Kakao kullanmak için, uygulamanız önce en az bir NSThread nesnesini ayırmalıdır; NSThread sınıf yöntemi isMultiThreaded ile Cocoa'nın çoklu iş parçacığı modunda olup olmadığını test edebilirsiniz.

...

Otomatik Referans Sayımı veya ARC'de sistem, MRR ile aynı referans sayma sistemini kullanır, ancak derleme zamanında sizi uygun bellek yönetimi yöntemine çağırır. Yeni projeler için ARC'yi kullanmanız şiddetle tavsiye edilir. ARC kullanıyorsanız, bazı durumlarda yardımcı olabilmesine rağmen, genellikle bu belgede açıklanan temel uygulamayı anlamanız gerekmez. ARC hakkında daha fazla bilgi için, bkz. ARC Sürüm Notlarına Geçiş.


2

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];
}

autoreleaseBir 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 tempObjectForDatayeni 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 DispatchQueueveya 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.


-4

Bu konuda çok fazla kafa karışıklığı var gibi görünüyor (ve muhtemelen şu anda bu konuda kafası karışmış ve kodlarına @autoreleasepool serpmeleri gerektiğini düşünen en az 80 kişi).

Bir proje (bağımlılıkları dahil) yalnızca ARC kullanıyorsa, @autoreleasepool asla kullanılmaya ihtiyaç duymaz ve yararlı bir şey yapmaz. ARC, serbest bırakma nesnelerini doğru zamanda yönetecektir. Örneğin:

@interface Testing: NSObject
+ (void) test;
@end

@implementation Testing
- (void) dealloc { NSLog(@"dealloc"); }

+ (void) test
{
    while(true) NSLog(@"p = %p", [Testing new]);
}
@end

görüntüler:

p = 0x17696f80
dealloc
p = 0x17570a90
dealloc

Her Test nesnesi, değer otomatik olarak çıkma havuzundan çıkılmayı beklemeden değer kapsam dışına çıkar çıkmaz dağıtılır. (NSNumber örneğinde de aynı şey olur; bu sadece dealloc'u gözlemlememizi sağlar.) ARC otomatik yeniden başlatma kullanmaz.

@Autoreleasepool'a hala izin verilmesinin nedeni, henüz tamamen ARC'ye geçmemiş olan karma ARC ve ARC dışı projeler içindir.

ARC olmayan bir kodu ararsanız, otomatik olarak yayınlanan bir nesne döndürebilir. Bu durumda, mevcut otomatik yayın havuzundan asla çıkılmayacağından yukarıdaki döngü sızar. Kod bloğu etrafına @autoreleasepool koymak istediğiniz yer burasıdır.

Ancak eğer ARC geçişini tamamen yaptıysanız, otomatik yeniden başlatma havuzunu unutun.


4
Bu cevap yanlıştır ve ARC belgelerine de aykırıdır. kanıtınız anekdottur, çünkü derleyicinin otomatik olarak yayınlanmamasına karar verdiği bir ayırma yöntemi kullanıyorsunuzdur. Özel sınıfınız için yeni bir statik başlatıcı oluşturursanız bunun kolayca çalışmadığını görebilirsiniz. Bu başlatıcı oluşturun ve döngüde kullanmak: + (Testing *) testing { return [Testing new] }. Sonra dealloc'un daha sonraya kadar çağrılmayacağını göreceksiniz. Döngünün içini bir @autoreleasepoolbloğa sarırsanız bu sorun çözülür.
Dima

@Dima iOS10'da denendi, nesne adresi yazdırıldıktan hemen sonra dealloc çağrıldı. + (Testing *) testing { return [Testing new];} + (void) test { while(true) NSLog(@"p = %p", [self testing]);}
KudoCC

@KudoCC - Ben de gördüm ve aynı davranışları gördüm. Ancak, [UIImage imageWithData]denkleme attığımda, aniden, en üst hafızayı makul bir seviyede tutmayı autoreleasegerektiren geleneksel davranışı görmeye başladım @autoreleasepool.
Rob

@Rob Bağlantıyı eklememe kendime yardımcı olamıyorum .
KudoCC
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.