6

У меня есть свойство 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 
} 

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

+2

просто FYI, вы можете использовать '__weak TypeOf (само) * weakSelf = я;' –

+0

здорово, это полезно! – manmal

+4

Небольшая поправка к общей декларации '__weak typeof (self) weakSelf = self;' typeof (self) уже является указателем. – allprog

ответ

5

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

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

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

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

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

__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 равен нулю, чтобы предотвратить дорогостоящую задачу от исполнения, если она не имеет смысла (объект уже уничтожен). Но это зависит от варианта использования.

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

+0

спасибо! да, это iOS5, слава богу. – manmal

+0

isFinished - это свойство readonly, поэтому я не хочу, чтобы '' 'strongSelf.isFinished = YES''' работал .. он есть только для KVO – manmal

+1

Указать нормальное назначение в частной категории, это заставит его сделать isFinished writeable в коде вашего класса, но только для чтения, это также пропустит ненужные ручные уведомления KVO. –

0

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

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

- (void)makeIsFinishedYes 
{ 
    isFinished_ = YES; 
}