Слабо Аз в блокове

Трябва ли да проверя дали слабото аз е нула в блокове?

Създавам указател на weakSelf като:

__weak typeof(self) weakSelf = self;

и в началото на блоковете правя

if(!weakSelf){return;}

това ненужно ли е или зависи от това дали съм кодирал правилно останалите, така че когато себе си умре, другите също умират?


person EralpB    schedule 17.06.2015    source източник
comment
Зависи какво трябва да прави вашият блок. Като цяло, ако искате да сте сигурни, че блокът се изпълнява само когато wealSelf не е нула, вие се нуждаете от този ред!   -  person Nils Ziehn    schedule 17.06.2015
comment
по принцип всяко извикване на метод е валиден операнд на nil указател в Obj-C, така че не е необходимо да се проверява; като напр. ако weakself е nil, тогава такова извикване на метод няма да причини проблем във вашия блок: [weakself doSomething];.   -  person holex    schedule 17.06.2015


Отговори (3)


Тази проверка е ненужна и ви дава фалшиво чувство за сигурност.

Ето го проблема:

__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
    if (!weakSelf) { return; }
    // THE LINE OF INTEREST
    [weakSelf doSomething];
});

При THE LINE OF INTEREST друга нишка може да изчисти последната силна препратка към self, в който момент weakSelf е зададено на нула. Така че съобщението doSomething се изпраща до нула, което е „безопасно“ (не прави нищо), но може да не е това, което сте очаквали!

По-лошо е, ако искате да предприемете различно действие, когато weakSelf е нула, напр.

__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
    if (weakSelf) {
        [weakSelf doSomething];
    } else {
        [someOtherObject doSomethingElse];
    }
});

В този случай, между времето, когато блокът проверява, че weakSelf не е нула, и времето, когато изпрати съобщението doSomething, weakSelf може да стане нула и нито doSomething, нито doSomethingElse действително ще се изпълняват.

Правилното решение е следното:

__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
    typeof(self) strongSelf = weakSelf;
    if (strongSelf) {
        [strongSelf doSomething];
    } else {
        [someOtherObject doSomethingElse];
    }
});

В този случай копирането на weakSelf в strongSelf (което е силно по подразбиране) е атомарно. Ако weakSelf беше нула, strongSelf ще бъде нула. Ако weakSelf не е нула, strongSelf няма да бъде нула и ще бъде силна препратка към обекта, предотвратявайки освобождаването му преди съобщението doSomething.

person rob mayoff    schedule 17.06.2015
comment
Във вашия код можете просто да използвате self вместо weakSelf. - person Amin Negm-Awad; 17.06.2015
comment
Ако използвате self директно в блока, блокът улавя self силно, поддържайки го жив. Ако използвате weakSelf, блокът не улавя self силно и позволява да бъде освободен, преди блокът да се изпълни. - person rob mayoff; 17.06.2015
comment
Това е правилното. Така обектът просто живее по-дълго. Докато блокът трябва да бъде изпълнен. Обикновено това е точно това, което искате. - person Amin Negm-Awad; 17.06.2015
comment
Но понякога не е така. Съветът „просто да използвате self вместо weakSelf“ трябва да бъде квалифициран: понякога това ще промени поведението на програмата. Освен това, в случай че блокът се запазва за неопределено време, той може да създаде цикъл на запазване. Това няма да се случи в кода на моя отговор, но някои често използвани API (като NSNotificationCenter) запазват блока за неопределено време. EralpB не показа контекста, в който създава своя блок. Може да се наложи неговият случай на употреба да избегне цикъл на запазване. - person rob mayoff; 17.06.2015
comment
Не съм казал без думи: просто използвайте self вместо weakSelf. Казах: Във вашият код може просто да се използва self вместо weakSelf. Така че няма причина да се търсят примери за нещо, което не е предмет на дискусия. - person Amin Negm-Awad; 17.06.2015
comment
Този пример също е чудесна аналогия за разбиране на [weak self] прихващане в Swift затваряния. Вашето правилно решение е по същество същото като правенето на if let else незадължителна верига на self в рамките на бързо затваряне. - person Alex Chase; 01.08.2018

Изглежда доста ненужно, тъй като извикването на съобщение на nil е без операция. (Нищо не се случва)

^{
    [weakSelf doSomething]; //Does nothing if weakSelf is nil
}

Единствената причина, поради която смятам, че може да искате да направите това, е, ако не трябва да се извикват други съобщения (не на себе си)

^{
    // Here I don't want to add weakSelf as an observer if it's nil
    if (!weakSelf) return;

    [OtherClass addObserverForSomething:weakSelf];
}
person James Webster    schedule 17.06.2015

Слабите препратки не запазват посочения обект. Ако никой друг не го задържа, обектът се освобождава и слабите препратки се отнасят до nil.

Следователно е възможно вашият код да се изпълни с weakSelf, който препраща nil. Но това изобщо не е причина да го проверявате. Особено в Objective-C използвате дефинирано поведение, ако изпратите съобщение nil. т.е. това е перфектен код, ако зададете свойство, използвайки препратка nil. Просто отива за никъде.

Разбира се, понякога не искате да взаимодействате с nil. В такъв случай трябва да го проверите.

Между другото: Имате нужда от weakSelf само при някои, редки обстоятелства. Градска легенда е, че в затварянията като цяло препратките към self трябва да са слаби, за да предотвратят цикли на запазване. Не е било вярно, не е вярно и никога няма да е вярно.

person Amin Negm-Awad    schedule 17.06.2015