Короткий ответ
Вместо 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
как больше нигде не используется).
Что лучше, будет зависеть от того, какой код вы можете изменить, а что нет. Но, надеюсь, это дало вам некоторые идеи о том, как поступить.