В каких ситуациях нам нужно написать квалификатор владения __autoreleasing под ARC? - PullRequest
114 голосов
/ 14 января 2012

Я пытаюсь завершить головоломку.

__strong - это значение по умолчанию для всех указателей объектов Objective-C, таких как NSObject, NSString и т. Д. Это сильная ссылка.ARC уравновешивает его с -release в конце области.

__unsafe_unretained соответствует старому способу.Он используется для слабого указателя без сохранения сохраняемого объекта.

__weak похож на __unsafe_unretained, за исключением того, что это слабая ссылка с автоматическим обнулением, означающая, что указатель будет установлен равным нулю, как только объект, на который имеется ссылкаосвобожден.Это устраняет опасность висячих указателей и ошибок EXC_BAD_ACCESS.

Но для чего именно __autoreleasing хорош?Мне трудно найти практические примеры того, когда мне нужно использовать этот классификатор.Я считаю, что это только для функций и методов, которые ожидают указатель-указатель, такой как:

- (BOOL)save:(NSError**);

или

NSError *error = nil;
[database save:&error];

, которые в ARC должны быть объявлены следующим образом:

- (BOOL)save:(NSError* __autoreleasing *);

Но это слишком расплывчато, и я хотел бы полностью понять почему .Фрагменты кода, которые я нахожу, помещают __autoreleasing между двумя звездами, что мне кажется странным.Типом является NSError** (указатель-указатель на NSError), так почему же ставить __autoreleasing между звездами, а не просто перед NSError**?

Также могут быть другие ситуации, в которых я должен полагаться на __autoreleasing.

Ответы [ 3 ]

67 голосов
/ 14 января 2012

Ты прав.Как объясняется в официальной документации:

__ 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.

34 голосов
/ 01 ноября 2012

В ответ на ответ Macmade и последующий вопрос гордого участника в комментариях (также разместил бы это как комментарий, но он превышает максимальное количество символов):

Вот почему квалификатор переменной __autoreleasingнаходится между двумя звездами.

Для предисловия правильный синтаксис объявления указателя объекта с квалификатором:

NSError * __qualifier someError;

Компилятор простит это:

__qualifier NSError *someError;

но это не правильно.См. руководство по переходу Apple ARC (прочитайте раздел, который начинается «Вы должны правильно декорировать переменные ...»).

Чтобы обратиться к рассматриваемому вопросу: двойной указатель не может иметьСпецификатор управления памятью ARC, потому что указатель, который указывает на адрес памяти, является указателем на тип примитива, а не указателем на объект.Однако, когда вы объявляете двойной указатель, ARC хочет знать, каковы правила управления памятью для второго указателя.Вот почему переменные двойного указателя задаются как:

SomeClass * __qualifier *someVariable;

Так что в случае аргумента метода, который является двойным указателем NSError, тип данных объявляется как:

- (BOOL)save:(NSError* __autoreleasing *)errorPointer;

, чтона английском говорит «указатель на __autoreleasing указателя объекта NSError».

15 голосов
/ 25 июля 2012

Определенная спецификация ARC говорит, что

Для объектов __autoreleasing новый указатель сохраняется, автоматически высвобождается и сохраняется в lvalue с использованием примитивной семантики.

Так, например, код

NSError* __autoreleasing error = someError;

фактически преобразуется в

NSError* error = [[someError retain] autorelease];

..., поэтому он работает, когда у вас есть параметр NSError* __autoreleasing * errorPointer,Затем вызываемый метод назначит ошибку *errorPointer, и сработает вышеуказанная семантика.

Вы можете использовать __autoreleasing в другом контексте, чтобы принудительно включить объект ARC в пул авто-выпуска, но это не очень полезнопоскольку ARC, по-видимому, использует пул автоматического выпуска при возврате метода и уже обрабатывает это автоматически.

...