В каких ситуациях нужно писать autoreleasing собственности квалификатор под дуги?
Я пытаюсь завершить головоломки.
__strong
является значением по умолчанию для всех указателей Objective-C retainable object, таких как NSObject, NSString и т. д.. Это сильная ссылка. Дуга уравновешивает его с -release
в конце рамки.
__unsafe_unretained
равен старому. Он используется для слабого указателя без сохранения удерживаемого объекта.
__weak
как __unsafe_unretained
за исключением того, что это авто-обнуление слабой ссылки означает, что указатель будет установлен к нулю, как только объект ссылки освобождается. Это устраняет опасность висячих указателей и ошибок EXC_BAD_ACCESS.
но что же такое __autoreleasing
хороши? Мне трудно найти практические примеры, когда мне нужно использовать этот квалификатор. Я считаю, что это только для функций и методов, которые ожидают указатель-указатель, такие как:
- (BOOL)save:(NSError**);
или
NSError *error = nil;
[database save:&error];
который под дугой должен быть объявлен этим образом:
- (BOOL)save:(NSError* __autoreleasing *);
но это слишком расплывчато, и я хотел бы полностью понять почему. Фрагменты кода, которые я нахожу, помещают _ _ autoreleasing между двумя звездами, что выглядит странно для меня. Тип -NSError**
(указатель-указатель на NSError), так зачем размещать __autoreleasing
между звездами, а не просто перед NSError**
?
кроме того, там могут быть и другие ситуации, в которых я должен полагаться на __autoreleasing
.
3 ответа:
вы правы. Как поясняет официальная документация:
__autoreleasing для обозначения аргументов, которые передаются по ссылке (id *) и автоматически освобождаются при возврате.
все это очень хорошо объясняется в руководство по переходу дуги.
в вашем примере 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;
но это не правильно. Видеть руководство по переходу дуги Яблока (прочитайте раздел, который начинается "Вы должны украсить переменные правильно...").
чтобы обратиться к рассматриваемому вопросу: двойной указатель не может иметь квалификатор управления памятью ARC, потому что указатель, указывающий на адрес памяти, является указателем на примитивный тип, а не указателем на объект. Однако, когда вы объявляете двойной указатель, ARC действительно хочет знать, какие правила управления памятью для второго указателя. Вот почему переменные двойного указателя задаются как:
SomeClass * __qualifier *someVariable;
поэтому в случае аргумента метода, который является двойным указателем NSError, тип данных объявляется как:
- (BOOL)save:(NSError* __autoreleasing *)errorPointer;
который на английском языке говорит "указатель на указатель объекта _ _ autoreleasing NSError".
The окончательная спецификация дуги говорит, что
для объектов _ _ autoreleasing новый указатель сохраняется, автореализуется и сохраняется в lvalue с использованием примитивной семантики.
так, например, код
NSError* __autoreleasing error = someError;
фактически преобразуется в
NSError* error = [[someError retain] autorelease];
... именно поэтому он работает, когда у вас есть параметр
NSError* __autoreleasing * errorPointer
, вызванный метод затем присвоит ошибку*errorPointer
и выше семантики будет брыкаться.вы могли бы использовать
__autoreleasing
в другом контексте, чтобы заставить объект дугу в autorelease пул, но это не очень полезно, Так как дуги только кажется, использовать autorelease пул в способ вернуться и уже обрабатывает это автоматически.