Подтвердить что ты не робот

IOS 4 блокирует и удерживает счета

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

Например:

^{  
    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];
}
4b9b3361

Ответ 1

Нет. Проблема возникает, когда ваш блок сохраняет объект, который сохраняет его. Ваш блок сохранит любой объект, который он ссылается, кроме тех, которые были аннотированы с помощью __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

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