Присвоение ivar в блоке через слабый указатель

У меня есть свойство isFinished только для чтения в моем файле интерфейса:

typedef void (^MyFinishedBlock)(BOOL success, NSError *e);

@interface TMSyncBase : NSObject {
     BOOL isFinished_;
}

@property (nonatomic, readonly) BOOL isFinished;

и я хочу установить его на YES в блоке в какой-то момент позже, не создавая цикл сохранения на self:

- (void)doSomethingWithFinishedBlock:(MyFinishedBlock)theFinishedBlock {
    __weak MyClass *weakSelf = self;
    MyFinishedBlock finishedBlockWrapper = ^(BOOL success, NSError *e) {
        [weakSelf willChangeValueForKey:@"isFinished"];
        weakSelf -> isFinished_ = YES;
        [weakSelf didChangeValueForKey:@"isFinished"];
        theFinishedBlock(success, e);
    };

    self.finishedBlock = finishedBlockWrapper; // finishedBlock is a class ext. property
}

Я не уверен, что это правильный способ сделать это. Будет ли этот код протекать или ломаться, или все в порядке? Возможно, есть более простой способ, который я упустил из виду?


comment
просто к сведению, вы можете использовать __weak typeof(self) *weakSelf = self;   -  person Martin S.    schedule 04.07.2012
comment
Небольшая поправка к общему объявлению __weak typeof(self) weakSelf = self; typeof(self) уже является указателем.   -  person allprog    schedule 05.01.2013


Ответы (2)


Передаваемая блочная переменная может быть нулевой, проверьте перед вызовом или добавьте утверждение при запуске функции, иначе произойдет сбой.

Поскольку вы не сохраняете себя, и мы предполагаем, что вы выполняете какую-то длинную задачу в фоновом потоке к тому времени, когда ваш код будет выполнен, weakSelf может быть равен нулю (надеюсь, вы используете ARC и 5.0, поэтому у вас есть слабые ссылки).

Если у вас нет реальных слабых ссылок (‹ 5.0, без ARC, компилятор все равно примет __weak, но это не имеет значения), это приведет к сбою.

Также доступ к ivar с использованием '->' приведет к сбою, если указатель объекта равен нулю, поэтому вам нужно убедиться, что этого не произойдет.

Даже если вы сделаете код, как написал dasblinkenlight, он может рухнуть, если weakSelf будет равен нулю в данный момент, скажем, вы отправляете блок в фоновом потоке, а затем объект освобождается до выполнения блока, это делает weakSelf нулевым, таким образом получая доступ к нему с помощью '- >' приведет к сбою. В этом случае я бы изменил код следующим образом:

__weak MyClass *weakSelf = self;
MyFinishedBlock finishedBlockWrapper = ^(BOOL success, NSError *e) {
    MyClass *strongSelf = weakSelf;
    //! whatever task you want executed
    strongSelf.isFinished = YES;
    theFinishedBlock(success, e);
};

Также вы можете проверить, является ли weakSelf нулевым, чтобы предотвратить выполнение дорогостоящей задачи, если это не имеет смысла (объект уже уничтожен). Но это зависит от варианта использования.

Но есть и другой случай, который необходимо учитывать при программировании с блоками, например: у вас может быть экземпляр объекта задания, единственной ролью которого является выполнение какой-либо задачи в фоновом режиме, в этом случае этот код может дать сбой, потому что вы создадите новую задачу и он может быть освобожден до того, как блок будет выполнен в фоновом потоке, в этом случае вы должны сохранить себя и не сохранять блок в объекте (это предотвратит сохранение цикла).

person Krzysztof Zabłocki    schedule 04.07.2012
comment
isFinished — это свойство только для чтения, поэтому я не хочу, чтобы strongSelf.isFinished = YES работало.. оно предназначено только для KVO - person manmal; 04.07.2012
comment
Specify — это обычное назначение в частной категории, это сделает isFinished доступным для записи в коде вашего класса, но только для чтения вне его, это также пропустит ненужные ручные уведомления KVO. - person Krzysztof Zabłocki; 04.07.2012
comment
так зачем strongSelf нужно? - person newacct; 05.07.2012
comment
strongSelf сохранит объект на время выполнения тела блока, если при входе объект все еще существовал. Таким образом, объект не будет выпущен в середине вашего блочного кода. - person Krzysztof Zabłocki; 05.07.2012

Небольшой обходной путь — создать метод и позволить компилятору обработать его за вас. Работает нормально, но я не уверен, что это правильный путь. Может кто-нибудь сказать, правильно ли это?

__weak MyClass *weakSelf = self;
MyFinishedBlock finishedBlockWrapper = ^(BOOL success, NSError *e) {
    [weakSelf makeIsFinishedYes];
};

- (void)makeIsFinishedYes
{
    isFinished_ = YES;
}
person vivek241    schedule 07.05.2013