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