Боюсь, этот вопрос довольно простой, но я думаю, что он актуален для многих программистов Objective-C, которые разбираются в блоках.
Я слышал, что поскольку блоки захватывают локальные переменные, на которые они ссылаются как const
копии, использование self
внутри блока может привести к циклу сохранения, если этот блок будет скопирован. Таким образом, мы должны использовать __block
блок для непосредственного взаимодействия с ним self
вместо его копирования.
__block typeof(self) bself = self;
[someObject messageWithBlock:^{ [bself doSomething]; }];
вместо просто
[someObject messageWithBlock:^{ [self doSomething]; }];
Я хотел бы знать следующее: если это правда, могу ли я избежать уродства (кроме использования GC)?
objective-c
memory-management
objective-c-blocks
Джонатан Стерлинг
источник
источник
self
доверенным лицам,this
просто чтобы перевернуть вещи. В JavaScript я называю своиthis
замыканияself
, так что это приятно и сбалансировано. :)Ответы:
Строго говоря, тот факт, что это константная копия, не имеет ничего общего с этой проблемой. Блоки сохранят все значения obj-c, которые были получены при их создании. Так уж сложилось, что обходной путь для проблемы const-copy идентичен обходному пути для проблемы сохранения; а именно, используя
__block
класс хранения для переменной.В любом случае, чтобы ответить на ваш вопрос, здесь нет реальной альтернативы. Если вы разрабатываете свой собственный API на основе блоков, и это имеет смысл сделать, вы можете передать блоку значение
self
in в качестве аргумента. К сожалению, это не имеет смысла для большинства API.Обратите внимание, что ссылка на ivar имеет точно такую же проблему. Если вам нужно сослаться на ivar в вашем блоке, используйте вместо этого свойство или используйте
bself->ivar
.Приложение: При компиляции как ARC
__block
больше не прерывается сохранение циклов. Если вы компилируете для ARC, вам нужно использовать__weak
или__unsafe_unretained
вместо.источник
__weak
тоже хорошо. Если вы точно знаете, что объект не может быть вне области видимости при вызове блока, то__unsafe_unretained
он немного быстрее, но в целом это не имеет значения. Если вы используете__weak
, убедитесь, что вы выбросили ее в__strong
локальную переменную и проверьте ее на отсутствие,nil
прежде чем что-либо делать с ней.__block
Побочный эффект не сохранения и освобождения был вызван неспособностью правильно рассуждать об этом. С ARC компилятор получил эту способность, и__block
теперь сохраняет и выпускает. Если вам нужно этого избежать, вам нужно использовать__unsafe_unretained
команду, которая указывает компилятору не выполнять какие-либо сохранения или освобождения значения в переменной.Просто используйте:
Для получения дополнительной информации: WWDC 2011 - Блоки и Grand Central Диспетчер на практике .
https://developer.apple.com/videos/wwdc/2011/?id=308
Примечание: если это не сработает, вы можете попробовать
источник
Это может быть очевидным, но вы должны делать уродливый
self
псевдоним только тогда, когда знаете, что получите цикл сохранения. Если блок - всего лишь один выстрел, то я думаю, что вы можете спокойно проигнорировать сохранениеself
. Плохой случай, когда у вас есть блок в качестве интерфейса обратного вызова, например. Как здесь:Здесь API не имеет особого смысла, но это имело бы смысл, например, при взаимодействии с суперклассом. Мы сохраняем обработчик буфера, обработчик буфера сохраняет нас. Сравните с чем-то вроде этого:
В этих ситуациях я не делаю
self
псевдонимов. Вы получите цикл сохранения, но операция будет недолгой, и блок со временем выйдет из памяти, прерывая цикл. Но мой опыт работы с блоками очень мал, и, возможно,self
в долгосрочной перспективе псевдонимы станут лучшей практикой.источник
copy
, а неretain
. Если они простоretain
, то нет никакой гарантии, что они будут удалены из стека, а это означает, что когда вы приступите к его выполнению, его больше не будет. (и копирование, и уже скопированный блок оптимизирован для сохранения)retain
фазу некоторое время назад и быстро понял, что вы говорите :) Спасибо!retain
полностью игнорируется для блоков (если только они не покинули стекcopy
).Опубликовать другой ответ, потому что это было проблемой и для меня. Первоначально я думал, что должен использовать blockSelf везде, где есть внутренняя ссылка внутри блока. Это не тот случай, это только когда сам объект имеет блок в нем. И на самом деле, если вы используете blockSelf в этих случаях, объект может получить dealloc'd до того, как вы получите результат обратно из блока, а затем он потерпит крах, когда попытается вызвать его, поэтому ясно, что вы хотите сохранить себя до ответа возвращается.
Первый случай демонстрирует, когда произойдет цикл сохранения, поскольку он содержит блок, на который ссылается блок:
Вам не нужен blockSelf во втором случае, потому что в вызывающем объекте нет блока, который вызовет цикл сохранения, когда вы ссылаетесь на self:
источник
self
не могут быть вызваны тем, что люди слишком применяют это исправление. Это хороший пример того, как избежать циклов сохранения в не-ARC-коде, спасибо за публикацию.Помните также, что циклы сохранения могут происходить, если ваш блок ссылается на другой объект, который затем сохраняет
self
.Я не уверен, что Сборка мусора может помочь в этих циклах сохранения. Если объект, сохраняющий блок (который я назову объектом сервера), переживет
self
(объект клиента), ссылка наself
внутри блока не будет считаться циклической до тех пор, пока сам объект сохранения не будет освобожден. Если объект сервера далеко переживает своих клиентов, у вас может быть значительная утечка памяти.Поскольку нет чистых решений, я бы порекомендовал следующие обходные пути. Не стесняйтесь выбирать один или несколько из них, чтобы решить вашу проблему.
doSomethingAndWhenDoneExecuteThisBlock:
, а не методы likesetNotificationHandlerBlock:
. Блоки, используемые для завершения, имеют определенный срок службы и должны быть освобождены объектами сервера после их оценки. Это предотвращает слишком длительный цикл сохранения, даже если он происходит.dealloc
вместоrelease
.Если вы пишете объект сервера, принимайте аргументы блока только для завершения. Не принимайте аргументы блока для обратных вызовов, таких как
setEventHandlerBlock:
. Вместо этого вернитесь к классическому шаблону делегата: создайте формальный протокол и объявитеsetEventDelegate:
метод. Не оставляйте делегата. Если вы даже не хотите создавать формальный протокол, примите селектор в качестве делегата обратного вызова.И наконец, эта схема должна вызывать сигналы тревоги:
Если вы пытаетесь отцепить блоки, которые могут ссылаться
self
изнутриdealloc
, у вас уже есть проблемы.dealloc
никогда не может быть вызван из-за цикла сохранения, вызванного ссылками в блоке, что означает, что ваш объект будет просто утекать, пока объект сервера не будет освобожден.источник
__weak
правильно.__block __unsafe_unretained
Модификаторы, предложенные в сообщении Кевина, могут привести к исключению плохого доступа в случае блока, выполненного в другом потоке. Лучше использовать только модификатор __block для переменной temp и сделать его нулевым после использования.источник
Вы можете использовать библиотеку libextobjc. Он довольно популярен, например, в ReactiveCocoa. https://github.com/jspahrsummers/libextobjc
Он предоставляет 2 макроса @weakify и @strongify, так что вы можете иметь:
Это предотвращает прямую сильную ссылку, поэтому мы не входим в цикл сохранения себя. Кроме того, он предотвращает обнуление себя на полпути, но все равно правильно уменьшает счет удержания. Подробнее по этой ссылке: http://aceontech.com/objc/ios/2014/01/10/weakify-a-more-elegant-solution-to-weakself.html
источник
Как насчет этого?
Я больше не получаю предупреждение компилятора.
источник
Блок: цикл сохранения будет происходить, потому что он содержит блок, на который ссылается блок; Если вы сделаете копию блока и используете переменную-член, self сохранится.
источник