iOS 4 блокирует и сохраняет счет - PullRequest
19 голосов
/ 03 апреля 2011

Я только начинаю с блоками и Grand Central Dispatch. Мне сказали (и прочитали в Документация Apple ), что любой объект, на который есть ссылка из блока, сохраняется.

Например:

^{  
    self.layer.transform = CATransform3DScale(CATransform3DMakeTranslation(0, 0, 0), 1, 1, 1);
    self.layer.opacity = 1;
}

"Я" сохраняется, поэтому оно протекает. Чтобы избежать этого, мне нужно присвоить себя:

__block Object *blockSelf = self;

и затем используйте blockSelf вместо self внутри моего блока.

Мой вопрос: что происходит, когда в вашем блоке намного больше кода и он ссылается на несколько объектов? Нужно ли присваивать их всем __block объектам? Например:

^{  
    [self doSomething];

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"prevName == %@", artistName];
    [request setEntity:entity];
    [request setPredicate:predicate];

    Object *newObject = [[Object alloc] init];
    [someArray addObject];
    [newObject release];
}

Ответы [ 2 ]

48 голосов
/ 03 апреля 2011

Нет. Проблема возникает, когда ваш блок сохраняет объект, который сохраняет его. Ваш блок сохранит любой объект, на который он ссылается, кроме тех, которые отмечены __block. Следовательно:

// The following creates a retain cycle which will leak `self`:
self.block = ^{
  [self something];
};

self сохраняет block, а block неявно сохраняет self. Это также произойдет, если вы ссылаетесь на переменные экземпляра self.

// The following avoids this retain cycle:
__block typeof(self) bself = self;
self.block = ^{
  [bself something];
};

Переменные, помеченные __block, являются изменяемыми (для указателей, то есть адрес, на который они указывают, может быть изменен); в результате нет смысла сохранить этот объект, так как вам нужно рассматривать этот объект как локальную переменную (как в случае, он может быть переназначен, затрагивая объект вне области блока). Таким образом, __block не задерживается блоками.

Но теперь вы можете столкнуться с непредвиденными проблемами, если попытаетесь использовать этот блок определенным образом. Например, если вы решили каким-то образом отложить вызов этого блока, и self был освобожден к моменту выполнения этого блока, ваша программа потерпит крах, так как вы отправляете сообщение освобожденному объекту. В таком случае вам нужна слабая ссылка, которая не предоставляется "из коробки" в среде без сбора мусора!

Одним из решений является использование MAZeroingWeakRef для упаковки вашего блока; это обнулит указатель, так что вы просто отправите сообщения на nil, если попытаетесь отправить сообщение self после освобождения self:

MAZeroingWeakRef *ref = [MAZeroingWeakRef refWithTarget:self];
self.block = ^{
  [ref.target something];
};

Я также реализовал оболочку слабых ссылок в Objective-C ++ , которая обеспечивает преимущество более легкого синтаксиса:

js::weak_ref<SomeClass> ref = self;
self.block = ^{
  [ref something];
};

Поскольку js::weak_ref является шаблоном класса, вы получите удобную строгую типизацию (то есть вы получите предупреждения во время компиляции, если попытаетесь отправить ссылке сообщение, на которое она не отвечает к). Но у Майка MAZeroingWeakReference намного более зрелый, чем у меня, поэтому я советую использовать его, если вы не хотите испачкать руки.

Чтобы узнать больше о проблемах с __block и сценарии использования для слабых ссылок, прочитайте Как избежать сохранения циклов с блоками, правильный путь и Ответ Джонатана Рентша .

2 голосов
/ 03 апреля 2011

Я бы сказал, это зависит от того, что вы делаете со своим блоком. Если вы нигде не храните его и не используете его в месте определения (например, сортируете массив с помощью блока), то он освобождается вместе с переменными, на которые есть ссылки (так как он создан в стеке и помечен для автоматического выпуска). Если вы храните его где-то (массив, словарь или, возможно, передаете блок какой-то другой функции) copy блок и уравновешивайте его с помощью autorelease перед его передачей.

...