В каких ситуациях нужно писать 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 111

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 пул в способ вернуться и уже обрабатывает это автоматически.