bu blokta kendini güçlü bir şekilde yakalamak, bir tutma döngüsüne yol açacaktır.


207

Xcode bu uyarıyı nasıl önleyebilirim? Kod pasajı şöyledir:

[player(AVPlayer object) addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
queue:nil usingBlock:^(CMTime time) {
    current+=1;

    if(current==60)
    {
        min+=(current/60);
        current = 0;
    }

    [timerDisp(UILabel) setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];///warning occurs in this line
}];

timerDispsınıfın bir özellik?
Tim

Evet, @property (anatomik olmayan, güçlü) UILabel * timerDisp;
user1845209

2
Bu nedir: player(AVPlayer object)ve timerDisp(UILabel)?
Carl Veazey

AVPlayer * oynatıcı; UILabel * timerDisp;
user1845209

5
Asıl soru, dairesel referansın bozulacağını bildiğiniz zaman, kendinize gereksiz zayıf bir referans olmadan bu uyarının nasıl susturulacağıdır (örneğin, bir ağ isteği bittiğinde referansı her zaman temizlerseniz).
Glenn Maynard

Yanıtlar:


514

Yakalanması selfburadan sizin örtük özellik erişimi ile geliyor self.timerDisp- Eğer başvuruda bulunamaz selfveya özellikleri selfkuvvetle tarafından tutulacaktır bir blok içinden self.

selfBloğunuza erişmeden önce zayıf bir referans oluşturarak bu sorunu çözebilirsiniz timerDisp:

__weak typeof(self) weakSelf = self;
[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
                                     queue:nil
                                usingBlock:^(CMTime time) {
                                                current+=1;

                                                if(current==60)
                                                {
                                                    min+=(current/60);
                                                    current = 0;
                                                }

                                                 [weakSelf.timerDisp setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];
                                            }];

13
__unsafe_unretainedBunun yerine kullanmayı deneyin .
Tim

63
Çözülmüş. bunun yerine şunu kullanın: __unsafe_unretained typeof (self) poorSelf = self; Yardım için teşekkürler @Tim
user1845209 28:13

1
Güzel cevap, ama sizinle küçük bir sorun alıyorum: “benlik ya da benlikteki mülklerden, kendiniz tarafından güçlü bir şekilde korunacak bir blok içinden başvuramazsınız.” Bu kesinlikle doğru değil. Lütfen cevabımı aşağıya bakın. Daha iyi söylemek gerekirse, “kendinize atıfta bulunursanız çok dikkatli olmalısınız …”
Chris Suter

8
OP kodunda bir tutma döngüsü görmüyorum. Blok güçlü bir şekilde tutulmaz self, ana gönderme kuyruğu tarafından korunur. Yanlış mıyım?
erikprice

3
@erikprice: yanlış değilsin. Soruyu, bir tutma döngüsünün gerçek varlığı yerine, Xcode'un sunduğu hata ile ilgili olarak yorumladım ("Bu uyarıyı xcode'da nasıl önleyebilirim"). Sadece sağlanan snippet OP'de hiçbir tutma döngüsünün belirgin olmadığını söylemekte haklısınız.
Tim

52
__weak MyClass *self_ = self; // that's enough
self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){
    if (!error) {
       [self_ showAlertWithError:error];
    } else {
       self_.items = [NSArray arrayWithArray:receivedItems];
       [self_.tableView reloadData];
    }
};

Ve hatırlanması gereken çok önemli bir şey: örnek değişkenlerini doğrudan blokta kullanmayın, zayıf nesnenin özellikleri olarak kullanın, örnek:

self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){
        if (!error) {
           [self_ showAlertWithError:error];
        } else {
           self_.items = [NSArray arrayWithArray:receivedItems];
           [_tableView reloadData]; // BAD! IT ALSO WILL BRING YOU TO RETAIN LOOP
        }
 };

ve yapmayı unutma:

- (void)dealloc {
    self.loadingCompletionHandler = NULL;
}

herhangi bir nesne tarafından tutulmadığının zayıf bir kopyasını geçirirseniz başka bir sorun ortaya çıkabilir:

MyViewController *vcToGo = [[MyViewCOntroller alloc] init];
__weak MyViewController *vcToGo_ = vcToGo;
self.loadingCompletion = ^{
    [vcToGo_ doSomePrecessing];
};

Eğer vcToGotahsis edilecek ve daha sonra bu blok ateş Ben vcToGo_şimdi değişken içeren bir çöp kutusu için tanınmayan seçici ile çökecek inanıyorum . Kontrol etmeye çalışın.


3
Eğer açıklarsanız bu daha güçlü bir cevap olacaktır.
Eric J.

43

Daha iyi sürüm

__strong typeof(self) strongSelf = weakSelf;

Bloğunuzdaki ilk satır olarak bu zayıf sürüme güçlü bir referans oluşturun. Blok yürütülmeye başladığında ve sıfır değerine düşmediğinde hala kendilik varsa, bu çizgi bloğun yürütme ömrü boyunca devam etmesini sağlar.

Yani her şey şöyle olurdu:

// Establish the weak self reference
__weak typeof(self) weakSelf = self;

[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
                                 queue:nil
                            usingBlock:^(CMTime time) {

    // Establish the strong self reference
    __strong typeof(self) strongSelf = weakSelf;

    if (strongSelf) {
        [strongSelf.timerDisp setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];
    } else {
        // self doesn't exist
    }
}];

Bu makaleyi defalarca okudum. Bu mükemmel bir makale Erica Sadun üzerinde Bloklar Ve NSNotificationCenter Kullanırken Sorunları Nasıl kaçının


Hızlı güncelleme:

Örneğin, hızlı bir şekilde başarı bloğuyla basit bir yöntem şöyle olur:

func doSomeThingWithSuccessBlock(success: () -> ()) {
    success()
}

Bu yöntemi çağırdığımızda ve selfbaşarı bloğunda kullanmamız gerektiğinde . [weak self]Ve guard letözelliklerini kullanacağız .

    doSomeThingWithSuccessBlock { [weak self] () -> () in
        guard let strongSelf = self else { return }
        strongSelf.gridCollectionView.reloadData()
    }

Bu güçlü-zayıf dans popüler açık kaynak projesi tarafından kullanılır Alamofire.

Daha fazla bilgi için hızlı stil rehberine göz atın


typeof(self) strongSelf = self;Bloktan sonra (__weak yerine) yaptıysanız, strongSelf = nil;kullanımdan sonra blokta söylerseniz ne olur ? Örneğiniz, blok yürütüldüğünde zayıfın kendisinin sıfır olmamasını nasıl sağladığını görmüyorum.
Matt

Olası tutma döngülerinden kaçınmak için, kodunda kendini kullanan tüm blokların dışında zayıf bir öz referans oluşturuyoruz. Ur şekilde, u bloğun yürütülmesini sağlamak zorunda. Başka bir ur kodu bloğu, daha önce tutulan belleği boşaltmaktan sorumludur.
Warif Akhand Rishi

@Matt bu örneğin amacı, zayıfın kendini korumasını sağlamak değildir. Amaç, eğer zayıfKirse, blok içinde güçlü bir referans yapmaktır. Böylece blok benlik ile çalışmaya başladığında benlik blok içinde sıfır olmaz.
Warif Akhand Rishi

15

Başka bir cevapta Tim şöyle dedi:

benlik ya da benlikteki özelliklere, kendiniz tarafından güçlü bir şekilde korunacak olan bir blok içinden başvuramazsınız.

Bu tam olarak doğru değil. Döngüyü bir noktada bozduğunuz sürece bunu yapmanız sorun değil. Örneğin, kendini koruyan bir bloğu olan bir zamanlayıcıya sahip olduğunuzu ve zamanlayıcıya kendi kendine güçlü bir referans verdiğinizi varsayalım. Zamanlayıcıyı bir noktada yok edeceğinizi ve döngüyü kıracağınızı her zaman biliyorsanız, bu gayet iyi.

Benim durumumda şimdi, ben yaptım kod için bu uyarı vardı:

[x setY:^{ [x doSomething]; }];

Şimdi clang'ın bu uyarıyı ancak metodun “set” (ve burada bahsetmeyeceğim bir başka özel durum) ile başladığını tespit etmesi durumunda üreteceğini biliyorum. Benim için, bir tutma döngüsü olma tehlikesi olmadığını biliyorum, bu yüzden yöntem adını “useY:” olarak değiştirdim. Tabii ki, bu her durumda uygun olmayabilir ve genellikle zayıf bir referans kullanmak isteyeceksiniz, ancak Başkalarına yardımcı olması durumunda çözümümü belirtmeye değer olduğunu düşündüm.


4

Çoğu zaman, bu aslında bir tutma döngüsü değildir .

Eğer olmadığını biliyorsan, dünyaya meyvesiz zayıflar getirmemelisin.

Apple bile bu uyarıları API ile üzerlerine zorlar; bu UIPageViewController, başka bir yerde belirtildiği gibi (bu uyarıları tetikler - bir blok olan bir ivar'a bir değer ayarladığınızı düşünen) ve bir tamamlama işleyici bloğunu (içinde şüphesiz kendinize atıfta bulunursunuz).

Bu kod satırından uyarıyı kaldırmak için bazı derleyici yönergeleri aşağıdadır:

#pragma GCC diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
    [self.pageViewController setViewControllers:@[newViewController] direction:navigationDirection animated:YES completion:^(BOOL finished) {
        // this warning is caused because "setViewControllers" starts with "set…", it's not a problem
        [self doTheThingsIGottaDo:finished touchThePuppetHead:YES];
    }];
#pragma GCC diagnostic pop

1

Hassasiyeti ve stili geliştirmek için iki sent eklemek. Çoğu durumda self, büyük olasılıkla sadece bir kaydırıcıyı güncellemek için bu blokta yalnızca bir veya birkaç üye kullanırsınız . Döküm selfaşırı. Bunun yerine, sadece blok içinde gerçekten ihtiyacınız olan nesneleri açıkça belirtmek ve yayınlamak daha iyidir . Bunun bir örneği var Örneğin, UISlider*diyelim ki, _timeSlidersadece blok bildirimi önce aşağıdakileri yapın:

UISlider* __weak slider = _timeSlider;

Sonra sadece sliderblok içinde kullanın . Teknik olarak bu, potansiyel tutma döngüsünü içerideki tüm nesnelere değil, yalnızca ihtiyacınız olan nesneye daralttığı için daha kesindir self.

Tam örnek:

UISlider* __weak slider = _timeSlider;
[_embeddedPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 1)
     queue:nil
     usingBlock:^(CMTime time){
        slider.value = time.value/time.timescale;
     }
];

Ayrıca, büyük olasılıkla zayıf bir işaretçiye dökülen nesne zaten zayıf bir işaretçidir selfve bir tutma döngüsü olasılığını en aza indirir veya tamamen ortadan kaldırır. Yukarıdaki örnekte _timeSlider, aslında zayıf bir referans olarak depolanan bir özelliktir, örneğin:

@property (nonatomic, weak) IBOutlet UISlider* timeSlider;

Kodlama stili açısından, C ve C ++ ile olduğu gibi, değişken bildirimleri sağdan sola daha iyi okunur. Bildirmek SomeType* __weak variableolarak sola bu sırada sağdan daha doğal okur: variable is a weak pointer to SomeType.


1

Son zamanlarda bu uyarıyla karşılaştım ve biraz daha iyi anlamak istedim. Biraz deneme yanılma işleminden sonra "add" ya da "save" ile başlayan bir metottan kaynaklandığını keşfettim. Amaç C, "yeni", "ayırma", vb. İle başlayan yöntem adlarını, korunan bir nesneyi döndürme olarak ele alır, ancak "ekle" veya "kaydet" hakkında hiçbir şeyden bahsetmez (bulabilirim). Ancak, bu şekilde bir yöntem adı kullanırsanız:

[self addItemWithCompletionBlock:^(NSError *error) {
            [self done]; }];

Uyarıyı [kendi kendine yapılan] satırında göreceğim. Ancak, bu olmayacak:

[self itemWithCompletionBlock:^(NSError *error) {
    [self done]; }];

Devam edeceğim ve "__weak __typeof (self) poorSelf = self" yolunu nesneme başvurmak için kullanacağım, ancak geleceği beni ve / veya diğer geliştiricileri karıştıracağı için bunu yapmaktan gerçekten hoşlanmayacağım. Tabii ki, "add" (ya da "save") de kullanamadım ama yöntemin anlamını ortadan kaldırdığı için bu daha kötü.

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.