В каких ситуациях нам нужно написать квалификатор владения __autoreleasing под ARC?
Я пытаюсь завершить головоломку.
__strong
это значение по умолчанию для всех указателей объектов Objective-C, таких как NSObject, NSString и т. д. Это надежная ссылка. ARC уравновешивает его -release
в конце объема.
__unsafe_unretained
равняется старому Он используется для слабого указателя без сохранения сохраняемого объекта.
__weak
как __unsafe_unretained
за исключением того, что это слабая ссылка с автоматическим обнулением, означающая, что указатель будет установлен равным nil, как только объект, на который имеется ссылка, будет освобожден. Это устраняет опасность висячих указателей и ошибок EXC_BAD_ACCESS.
Но что именно __autoreleasing
хорош для? Мне трудно найти практические примеры того, когда мне нужно использовать этот классификатор. Я считаю, что это только для функций и методов, которые ожидают указатель-указатель, такие как:
- (BOOL)save:(NSError**);
или же
NSError *error = nil;
[database save:&error];
который в соответствии с ARC должен быть объявлен следующим образом:
- (BOOL)save:(NSError* __autoreleasing *);
Но это слишком расплывчато, и я хотел бы полностью понять, почему. Фрагменты кода, которые я нахожу, помещают __autoreleasing между двумя звездами, что мне кажется странным. Тип является NSError**
(указатель-указатель на NSError), так зачем ставить __autoreleasing
между звездами, а не просто перед NSError**
?
Кроме того, могут быть другие ситуации, в которых я должен положиться __autoreleasing
,
4 ответа
Ты прав. Как объясняется в официальной документации:
__autoreleasing для обозначения аргументов, которые передаются по ссылке (id *) и автоматически высвобождаются при возврате.
Все это очень хорошо объяснено в руководстве по переходу ARC.
В вашем примере NSError, объявление означает __strong
неявно:
NSError * e = nil;
Будет преобразовано в:
NSError * __strong error = nil;
Когда вы звоните save
метод:
- ( BOOL )save: ( NSError * __autoreleasing * );
Затем компилятор должен будет создать временную переменную, установленную в __autoreleasing
, Так:
NSError * error = nil;
[ database save: &error ];
Будет преобразовано в:
NSError * __strong error = nil;
NSError * __autoreleasing tmpError = error;
[ database save: &tmpError ];
error = tmpError;
Вы можете избежать этого, объявив объект ошибки как __autoreleasing
прямо.
Следуя ответу Macmade и последующему вопросу гордого участника в комментариях (также разместил бы это как комментарий, но он превышает максимальное количество символов):
Вот почему переменная __autoreleasing находится между двумя звездами.
В качестве предисловия правильный синтаксис для объявления указателя объекта с классификатором:
NSError * __qualifier someError;
Компилятор простит это:
__qualifier NSError *someError;
но это не правильно. См. Руководство по переходу Apple ARC (прочитайте раздел, который начинается "Вы должны правильно декорировать переменные...").
Чтобы обратиться к рассматриваемому вопросу: двойной указатель не может иметь спецификатор управления памятью ARC, потому что указатель, который указывает на адрес памяти, является указателем на тип примитива, а не указателем на объект. Однако, когда вы объявляете двойной указатель, ARC хочет знать, каковы правила управления памятью для второго указателя. Вот почему переменные с двумя указателями указываются так:
SomeClass * __qualifier *someVariable;
Таким образом, в случае аргумента метода, который является двойным указателем NSError, тип данных объявляется как:
- (BOOL)save:(NSError* __autoreleasing *)errorPointer;
который по-английски говорит "указатель на __autoreleasing указателя объекта NSError".
Окончательная спецификация ARC гласит, что
Для объектов __autoreleasing новый указатель сохраняется, автоматически высвобождается и сохраняется в lvalue с использованием примитивной семантики.
Так, например, код
NSError* __autoreleasing error = someError;
на самом деле превращается в
NSError* error = [[someError retain] autorelease];
... поэтому он работает, когда у вас есть параметр NSError* __autoreleasing * errorPointer
вызываемый метод назначит ошибку *errorPointer
и вышеупомянутая семантика вступит в силу.
Вы могли бы использовать __autoreleasing
в другом контексте принудительно помещать объект ARC в пул автоматического выпуска, но это не очень полезно, так как ARC, кажется, использует пул автоматического выпуска только при возврате метода и уже обрабатывает это автоматически.
Короче говоря: это только для совместимости с MRC.
Apple договорились, что в собственных библиотеках объекты, возвращаемые
**
всегда выпускаются автоматически. Таким образом, код будет нормально работать со старыми двоичными файлами (например, если у вас есть цель развертывания iOS 4) и наоборот.
MRC
код будет работать нормально с
ARC
двоичные файлы.
Итак, в заключение:
Вы никогда не должны использовать
__autoreleasing
: компилятор автоматически добавит его там, где это необходимоЕсли вы не собираетесь поддерживать код MRC, вам следует использовать
* __strong *
везде. Это убережет от краха семьи:@autoreleasingpool { *autorelesingOut = [@"crash maker" mutableCopy];//NSString * __autoreleasing *autorelesingOut; *strongOut = [@"it's ok" mutableCopy];//NSString * __strong *strongOut; //App will crash if autorelesingOut will be referenced outside of this autoreleasepool }