Почему передача слабой ссылки на блок предотвращает сохранение объекта?
Мы все знаем, что блоки сохраняют объекты, которые они захватывают. Мы также знаем, что этого можно избежать, передав слабую ссылку на объект в блок. Но почему это работает так? Сохранить объект - значит увеличить его количество на единицу. Почему имеет смысл передавать слабую ссылку? Будучи слабым или сильным, он все равно будет указывать на тот же объект, и количество сохраняемых объектов будет увеличиваться на блок. Я прав? Так почему же количество сохраняемых объектов не увеличивается, если мы передаем слабую ссылку на объект внутри блока? Как это работает внутри?
3 ответа
Вы можете рассматривать блок как объект, который имеет "переменную экземпляра" для каждой захваченной переменной, которая инициализируется значением соответствующей захваченной переменной во время создания блока.
В ARC "переменные экземпляра" блока имеют тот же спецификатор владения, что и соответствующая захваченная переменная. Так что, если захваченная переменная типа указатель на объект __strong
(по умолчанию), "переменная экземпляра" блока также __strong
, таким образом, он сохраняет объект, на который указывает время жизни блока. Если захваченная переменная типа указатель на объект __strong
"переменная экземпляра" блока также __weak
следовательно, это нулевая слабая ссылка на указанный объект.
Слабые ссылки не увеличивают количество сохраняемых файлов, слабые ссылки - это просто указатели на объект, и если объект больше не существует, то для слабого свойства устанавливается значение nil, ARC обрабатывает это. Я не верю, что количество сохраняемых объектов будет увеличено слабой ссылкой внутри блока.
Поскольку слабая ссылка не сохраняет сильную привязку к экземпляру, на который она ссылается, возможно, что этот экземпляр будет освобожден, пока слабая ссылка все еще ссылается на него. Следовательно, ARC автоматически устанавливает слабую ссылку на ноль, когда экземпляр, на который он ссылается, освобождается.
Ссылка на документацию: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html
Что касается конкретной информации о том, как ARC работает с блоками, я узнал об этом от Apple, что не сильно помогло с вашим вопросом: https://developer.apple.com/library/ios/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html
Однако этот параграф может быть полезен для понимания того, как блоки сохраняют свои локальные переменные:
Блоки называются замыканиями в других языках, таких как Python, Ruby и Lisp, потому что они инкапсулируют состояние при объявлении. Блок создает константную копию любой локальной переменной, на которую есть ссылка в его области видимости.
От: http://www.raywenderlich.com/9438/how-to-use-blocks-in-ios-5-tutorial-part-2
Возможно, вы захотите понять, как работает замыкание в целом.
Возьмите следующий код в качестве примера,
var name = "NSNoName"
NSLog("Original name: %@ <%p>", name, name)
let takeName: (String) -> Void -> () = {
name in
return {
NSLog("Name inside block: %@ <%p>", name, name)
}
}
let returnName = takeName(name)
name = "NSNoFame"
returnName()
NSLog("Changed name: %@ <%p>", name, name)
Изначально значением переменной name является "NSNoName". Когда я печатаю имя в это время, я получаю результат,
Original name: NSNoName <0x7f8fb86004a0>
У меня есть простое замыкание, которое принимает строку в качестве параметра. Я вызываю замыкание с тем же именем объекта, в результате блок создает свою собственную копию объекта. Затем я продолжаю изменять имя объекта, и теперь, если я вызываю блок, чтобы напечатать имя, блок имеет то же исходное значение, которое было ему передано. Но объект отличается, что означает, что блок создал новый объект с тем же значением.
Name inside block: NSNoName <0x7f8fb8602510>
Последний NSLog печатает другое значение, так как оно уже изменено и имеет другое значение,
Changed name: NSNoFame <0x7f8fb8603ae0>
Это причина, по которой вы хотите сообщить блоку создать слабую ссылку на объект, что означает, что если исходный объект больше не существует, то нулевой объект ссылки создается в блоке.
Хотя с Objective C, кажется, немного по-другому,
@interface TestViewController ()
@property (nonatomic, strong) NSString *name;
@end
@implementation TestViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.name = @"NSNoName";
NSLog(@"Original name: %@ <%p>", self.name, self.name);
typedef void(^ReturnNameBlock)();
ReturnNameBlock (^takeName)(NSString*) = ^ReturnNameBlock(NSString *name) {
return ^{
NSLog(@"Name inside block: %@ <%p>", name, name);
};
};
ReturnNameBlock returnName = takeName(self.name);
self.name = @"NSNoFame";
returnName();
NSLog(@"Changed name: %@ <%p>", self.name, self.name);
}
@end
Мой журнал выглядит так,
Original name: NSNoName <0x103ae34c0>
Name inside block: NSNoName <0x103ae34c0>
Changed name: NSNoFame <0x103ae3520>
Если вы посмотрите на журнал, блоку принадлежит исходный объект self.name, так как оба имеют одинаковый адрес памяти. Хотя viewController больше не владеет этим, когда мы изменяем self.name = "NSNoFame", блок все еще сохраняет тот же экземпляр объекта.
Разница в быстром и объективном состоит в том, что блок Objective C сохраняет исходный экземпляр объекта, переданного ему, в то время как быстрое закрытие создает копию исходной переменной экземпляра.