2013-07-23 2 views
7

Скажем, я пытаюсь получить доступ к self внутри блока:ARC __block и __weak

[someObject successBlock:^(NSArray *result) { 
    [self someSuccessMethod]; 
} failure:^(NSString *errorMessage, int status) { 
    [self someFailureMethod]; 
}]; 

Я понимаю, что это создает цикл сохранения и someObject и self никогда не де-alloced.

Что меня смущает, что на самом деле происходит с/без ключевого слова __block. Я могу исправить цикл сохранить, сделав __weak ссылку на себя:

__weak MyClass* me = self; 
[someObject successBlock:^(NSArray *result) { 
    [me someSuccessMethod]; 
} failure:^(NSString *errorMessage, int status) { 
    [me someFailureMethod]; 
}]; 

мне не нужно использовать __block здесь, потому что я не пытаюсь изменить me из блока. Из того, что я понимаю, если я не использую __block, в блоке указывается копия me. Мой вопрос: если то, что упоминается внутри блока, является просто копией объекта, почему исходный блок кода создает цикл сохранения? Я бы предположил, что ссылка на self - это просто копия, так как я никогда не использую ключевое слово __block. Я думаю об этом неправильно?

ответ

7

В первом случае блок захватывает self, то есть он сохраняет копию self как еще один сильный указатель. Это увеличивает количество удержаний заостренного объекта и вызывает цикл удержания.

Во втором случае блок захватывает me, т.е. он сохраняет копию me как другой слабого указателя. Это не увеличивает количество удержаний и поэтому не вызывает циклов удержания.

(Если вы печатаете адрес из me снаружи и внутри блока, вы увидите, что адреса различны. Блок имеет свой собственный слабый указатель на объект.)

Если pointed- для объекта освобождается, все слабые ссылки (в том числе сохраненные на блоке) устанавливаются в nil по времени выполнения Objective-C.

(я надеюсь, что я получил это право.)

+0

Предполагая, что MyCLass реализует копию, которая является реальной копией ... потому что '-copyWithZone:' может просто сохранить ... что совершенно законно и сделано практически в любом неизменяемом объекте. –

+0

@GradyPlayer: Возможно, я выразил себя плохо, но я имел в виду, что блок сохраняет сильный (или слабый) указатель в своем блочном контексте с текущим содержимым * 'self' (или' me'). Метод 'copy' * * не используется. –

+0

Да, иногда SO циклирует материал назад, когда кто-то что-то делает с ними ... и иногда у меня должен быть nit-pick месяцев или лет спустя ... но объекты могут быть скопированы при захвате блоков, поэтому я не думаю, что это неверно ... –

0

Вы можете путь себя в качестве аргумента блока, точно давая имя переменной «я», это защитит от selfretaining в блоке.

И у вас есть «someObject и self never get de-alloced»: self будет выпущен, когда блоки будут освобождены. Блоки будут освобождены с помощью некоторого объекта. SomeObject будет освобожден, если у него больше нет ссылок. Поэтому, если ваш самообъект владеет someObject, просто отпустите someObject, когда он вам больше не понадобится.

4

A Сохранение цикла происходит, когда два объекта хранят сильную ссылку друг на друга. Простейшим случаем является объект a, хранящий значительную ссылку на объект b и b, выполняющий противоположное [1]. Сохранение циклов является проблемой в Objective-C, поскольку они заставляют ARC полагать, что эти объекты всегда используются, даже если эти объекты не упоминаются нигде.

Давайте рассмотрим несколько примеров. У вас есть объект z, который выделяет a и b, использует их, а затем удаляет их.Если a и b создали цикл сохранения между собой, в первую очередь, a и b не будут освобождены. Если вы сделаете это несколько раз, вы будете серьезно утечка памяти.

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

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

Другое решение (как правило, менее изящное, но возможно подходящее в некоторых ситуациях) может иметь какой-то пользовательский метод cleanup в a, который ссылается на b. Таким образом, b будет освобожден, если вызывается cleanup (если b не упоминается в другом месте). Это громоздко, потому что вы не можете сделать это с a's dealloc (он никогда не вызывается, если есть цикл сохранения), и потому что вы должны помнить, чтобы позвонить в cleanup в соответствующее время.

  1. Следует отметить, что циклы сохраняют также транзитивно (например, объект a сильно ссылается b, которые сильно ссылается c, которые сильно ссылается a).

Со всем этим сказал: управление памятью блоков довольно сложно понять.

Ваш первый пример может создать временный сохранить цикл (и только если self объект сохраняет сильную ссылку на someObject). Этот временной цикл уходит, когда блок завершает выполнение и освобождается.

Во время выполнения self будет хранить ссылку на someObject, someObject к block и block к self снова. Но опять же, это только временно, потому что блок не постоянно хранится где-либо (если это не делает реализация [someObject successBlock:failure:], но это не часто для блоков завершения).

Таким образом, цикл сохранения не является проблемой в вашем первом примере.

Как правило, удержание циклов внутри блоков является проблемой только в том случае, если какой-либо объект хранит блок, а не выполняет его непосредственно. Тогда легко увидеть, что self сильно ссылается на block, а block имеет сильную ссылку на self. Обратите внимание, что доступ к любому ivar изнутри блока автоматически генерирует сильную ссылку на self в этом блоке.

Эквивалент тому, что содержащийся объект не ссылается на его контейнер, использует __weak SelfClass *weakSelf = self для доступа к обоим методам и иварам (тем лучше, если вы получаете доступ к иврагам через аксессуры, как при использовании свойств).Ссылка вашего блока на self будет слабой (это не копия, это слабая ссылка), и это позволит self освободить, если она больше не ссылается на ссылки.

Можно утверждать, что это хорошая практика всегда использовать weakSelf внутри всех блоков, сохраненных или нет, на всякий случай. Интересно, почему Apple не делала это по умолчанию. Выполнение этого обычно не делает ничего вредного для блочного кода, даже если оно фактически не используется.


__block редко используется на переменные, которые указывают на объекты, так как Objective-C не обеспечивает неизменность объектов, как это.

Если у вас есть указатель на объект, вы можете вызвать его методы, и эти методы могут его модифицировать, с или без __block. __block больше (только?) Полезен для переменных основных типов (int, float и т. Д.). См. here, что происходит, когда вы используете __block с переменной указателя объекта. Вы также можете узнать больше о __block в Blocks Programming Topics от Apple.

Редактировать: Исправлена ​​ошибка в отношении __block Использование указателей объектов. Спасибо @KevinDiTraglia за указание на это.

+1

Хороший ответ, но вы уверены в этом последнем заявлении? Я рассматриваю вопрос об использовании __block вместо __weak для ссылочного типа, и они имеют разные типы поведения, ссылка __weak становится нулевой, в то время как ссылка __block не делает этого. Я думаю, что это ближе к сильному указателю на ссылки на объекты. –

+0

Спасибо за ваш комментарий, вы правы. Я исправил эту часть ответа. –

+0

Не уверен, что при правильном использовании всегда слабая ссылка на себя. Иногда я думаю, что вы можете захотеть, чтобы этот блок сохранил ссылку, поэтому он не позволит ему освободиться. Насколько я понимаю, его следует использовать только при использовании сильной ссылки, что приведет к циклу сохранения. – Ixx

3

Ваш первый пример не будет создавать никогда конец цикла сохранения. Там будет цикл сохранения, все в порядке, но как только блоки будут выполнены, эталонная форма блоков на someObject будет удалена. Таким образом, someObject будет жить, по крайней мере, до тех пор, пока блоки не будут выполнены. Такое временное сохранение цикла может быть хорошо или плохо, в зависимости от того, что вы хотите:

Если вам нужен не someObject жив, по крайней мере до тех пор, его блоки доработок, это нормально. Однако, если нет причин держать этот объект, вы должны реализовать его, используя «слабую» ссылку.

Например. myObject - это контроллер представления, который в этих блоках извлекает изображение из сети. Если вы выберете someObject из контроллера навигации, контроллер не сможет отобразить изображение после его получения, поэтому его не нужно держать. Успех или ошибка неактуальны, пользователь больше не интересуется картиной someObject. В этом случае лучше использовать слабый вариант, однако код в блоках должен ожидать, что self может быть нулевым.

+1

Не более ли точно сказать, что после того, как блоки сделаны, ссылка на * их * удалена? – Ixx

+0

Это действительно правильно. +1, потому что это объясняет, почему он не создает цикл сохранения perm. Многие новые программисты всегда используют weakSelf, потому что они дезинформируются на циклах сохранения, как указано в принятом ответе. Хотя для большинства приложений это нормально, более сложные приложения будут видеть, что проблемы со ссылками освобождаются до того, как выполняется блок, вызывающий сбои, если вы попытаетесь ссылаться на эти объекты позже. Я думаю, вы хотели сказать в последнем предложении, что 'weakSelf' может быть нулевым. – Bot

Смежные вопросы