Короткий ответ
Вместо self
прямого доступа вы должны получить к нему косвенный доступ по ссылке, которая не будет сохранена. Если вы не используете автоматический подсчет ссылок (ARC) , вы можете сделать это:
__block MyDataProcessor *dp = self;
self.progressBlock = ^(CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
}
Эти __block
ключевые слова , знаки переменных , которые могут быть изменены внутри блока (мы не делаем) , но и они не сохраняются автоматически , когда блок сохраняется (если вы не используете ARC). Если вы сделаете это, вы должны быть уверены, что больше ничего не будет пытаться выполнить блок после освобождения экземпляра MyDataProcessor. (Учитывая структуру вашего кода, это не должно быть проблемой.) Узнайте больше о__block
.
Если вы используете ARC , семантика __block
изменений и ссылка будут сохранены, и в этом случае вы должны объявить это __weak
.
Длинный ответ
Допустим, у вас был такой код:
self.progressBlock = ^(CGFloat percentComplete) {
[self.delegate processingWithProgress:percentComplete];
}
Проблема здесь в том, что self сохраняет ссылку на блок; в то же время блок должен сохранять ссылку на себя, чтобы получить свое свойство делегата и отправить делегату метод. Если все остальное в вашем приложении освобождает ссылку на этот объект, его счетчик сохранения не будет равен нулю (потому что блок указывает на него), и блок не делает ничего плохого (потому что объект указывает на него), и так пара объектов попадет в кучу, занимая память, но навсегда недоступная без отладчика. Трагично, правда.
Этот случай можно легко исправить, выполнив вместо этого:
id progressDelegate = self.delegate;
self.progressBlock = ^(CGFloat percentComplete) {
[progressDelegate processingWithProgress:percentComplete];
}
В этом коде self сохраняет блок, блок сохраняет делегат, и циклов нет (видно отсюда; делегат может сохранить наш объект, но это сейчас не в наших руках). Этот код не будет подвергаться риску утечки таким же образом, потому что значение свойства делегата захватывается при создании блока, а не просматривается при его выполнении. Побочным эффектом является то, что если вы измените делегата после создания этого блока, блок все равно будет отправлять сообщения об обновлении старому делегату. Может ли это произойти или нет, зависит от вашего приложения.
Даже если вы были спокойны с таким поведением, вы все равно не можете использовать этот трюк в вашем случае:
self.dataProcessor.progress = ^(CGFloat percentComplete) {
[self.delegate myAPI:self isProcessingWithProgress:percentComplete];
};
Здесь вы передаете self
непосредственно делегату в вызове метода, поэтому вы должны получить его где-нибудь. Если у вас есть контроль над определением типа блока, лучше всего передать делегат в блок в качестве параметра:
self.dataProcessor.progress = ^(MyDataProcessor *dp, CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
};
Это решение позволяет избежать цикла сохранения и всегда вызывает текущий делегат.
Если вы не можете изменить блок, вы можете с этим справиться . Причина сохранения цикла является предупреждением, а не ошибкой, в том, что они не обязательно означают гибель для вашего приложения. Если MyDataProcessor
удастся освободить блоки после завершения операции, прежде чем родительский объект попытается освободить ее, цикл будет прерван, и все будет очищено должным образом. Если бы вы могли быть уверены в этом, то правильнее всего было бы использовать a #pragma
для подавления предупреждений для этого блока кода. (Или используйте флаг компилятора для каждого файла. Но не отключайте предупреждение для всего проекта.)
Вы также можете использовать аналогичный трюк, описанный выше, объявив ссылку слабой или не сохраненной и используя ее в блоке. Например:
__weak MyDataProcessor *dp = self; // OK for iOS 5 only
__unsafe_unretained MyDataProcessor *dp = self; // OK for iOS 4.x and up
__block MyDataProcessor *dp = self; // OK if you aren't using ARC
self.progressBlock = ^(CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
}
Все три из вышеперечисленного дадут вам ссылку без сохранения результата, хотя все они ведут себя немного по-разному: __weak
будут пытаться обнулить ссылку, когда объект будет освобожден; __unsafe_unretained
оставит вас с неверным указателем; __block
фактически добавит еще один уровень косвенности и позволит вам изменить значение ссылки изнутри блока (не имеет значения в этом случае, так dp
как больше нигде не используется).
Что лучше, будет зависеть от того, какой код вы можете изменить, а что нет. Но, надеюсь, это дало вам некоторые идеи о том, как поступить.
dp
будет освобожден (например, если это был контроллер представления и он был выдвинут), то строка[dp.delegate ...
вызовет EXC_BADACCESS?strong
илиweak
?@weakify(..)
и@strongify(...)
который позволяет вам использоватьself
в блоке без сохранения.Также есть возможность подавить предупреждение, когда вы уверены, что цикл будет прерван в будущем:
Таким образом , вы не должны обезьяны вокруг с
__weak
,self
сглаживание и явного Ивар префиксов.источник
__weak id weakSelf = self;
имеет принципиально иное поведение, чем подавление предупреждения. Вопрос начался с «... если вы уверены, что цикл сохранения будет нарушен»[array addObject:weakObject];
если слабый объект был выпущен, это вызывает сбой. Очевидно, что это не является предпочтительным по сравнению с циклом сохранения. Вы должны понять, действительно ли ваш блок живет достаточно долго, чтобы оправдать ослабление, а также хотите ли вы, чтобы действие в блоке зависело от того, действует ли слабый объект.Для общего решения у меня есть эти определения в заголовке прекомпиляции. Избегает захвата и все еще позволяет помощь компилятора, избегая использования
id
Тогда в коде вы можете сделать:
источник
self
внутри своего блока @weakify (self); id block = ^ {@strongify (self); [self.delegate myAPIDidFinish: self]; };Я считаю, что решение без ARC также работает с ARC, используя
__block
ключевое слово:РЕДАКТИРОВАТЬ: Согласно примечаниям к переходу на ARC , объект, объявленный с
__block
хранилищем, все еще сохраняется. Используйте__weak
(предпочтительно) или__unsafe_unretained
(для обратной совместимости).источник
__block
ключевое слово избегало сохранения его референта. Спасибо! Я обновил свой монолитный ответ. :-)Объединяя несколько других ответов, я сейчас использую это для типизированного слабого «я» в блоках:
Я установил это как фрагмент кода XCode с префиксом завершения «welf» в методах / функциях, который срабатывает после ввода только «мы».
источник
warning => "захват себя внутри блока может привести к циклу сохранения"
когда вы ссылаетесь на себя или его свойство внутри блока, который сильно сохраняет себя, чем это показано выше, предупреждение.
поэтому, чтобы избежать этого, мы должны сделать это недельный реф
поэтому вместо использования
мы должны использовать
примечание: сохранение цикла обычно происходит, когда некоторые, как два объекта, ссылающиеся друг на друга, с помощью которых оба имеют счетчик ссылок = 1, и их метод delloc никогда не вызывается.
источник
Новый способ сделать это с помощью @weakify и @strongify marco
Подробнее о @Weakify @Strongify Marco
источник
Если вы уверены, что ваш код не создаст цикл сохранения или что цикл будет прерван позже, то самый простой способ заставить предупреждение замолчать:
Причина, по которой это работает, заключается в том, что хотя точечный доступ к свойствам учитывается анализом XCode, и поэтому
считается сохраняемым по x of y (слева от присвоения) и y y x (справа), вызовы методов не подвергаются такому же анализу, даже если они являются вызовами методов доступа к свойству которые эквивалентны точке доступа, даже когда эти методы доступа к свойству генерируются компилятором, поэтому в
только правая сторона рассматривается как создающая сохранение (по y of x), и предупреждение о цикле сохранения не генерируется.
источник