В каких ситуациях нам нужно написать квалификатор собственности __autoreleasing под ARC?
Я пытаюсь закончить головоломку.
__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
.
Ответы
Ответ 1
Ты прав. Как поясняется в официальной документации:
__ 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
, напрямую.
Ответ 2
Следуя ответам Macmade и Вопросам правдома в комментариях, (также разместил бы это как комментарий, но он превысил максимальное количество символов):
Вот почему разделитель переменных __autoreleasing помещается между двумя звездами.
В предисловии правильный синтаксис для объявления указателя объекта с помощью классификатора:
NSError * __qualifier someError;
Компилятор простят это:
__qualifier NSError *someError;
но это неверно. См. руководство по переходу Apple ARC (прочитайте раздел, который начинается "Вы должны правильно декорировать переменные..." ).
Чтобы обратиться к вопросу: двойной указатель не может иметь квалификатор управления памятью ARC, потому что указатель, указывающий на адрес памяти, является указателем на примитивный тип, а не указателем на объект. Однако, когда вы объявляете двойной указатель, ARC хочет знать, какие правила управления памятью предназначены для второго указателя. Вот почему переменные двойного указателя указываются как:
SomeClass * __qualifier *someVariable;
Итак, в случае аргумента метода, который является двойным указателем NSError, тип данных объявляется как:
- (BOOL)save:(NSError* __autoreleasing *)errorPointer;
который на английском языке говорит "указатель на указатель объекта __autoreleasing NSError".
Ответ 3
окончательная спецификация ARC говорит, что
Для объектов __autoreleasing новый указатель сохраняется, автореализован и сохраняется в lvalue с использованием примитивной семантики.
Так, например, код
NSError* __autoreleasing error = someError;
фактически преобразуется в
NSError* error = [[someError retain] autorelease];
... поэтому он работает, когда у вас есть параметр NSError* __autoreleasing * errorPointer
, вызываемый метод затем назначит ошибку *errorPointer
, и эта семантика будет нажата.
Вы можете использовать __autoreleasing
в другом контексте, чтобы принудительно включить объект ARC в пул автозаполнения, но это не очень полезно, поскольку ARC только, кажется, использует пул автозаполнения при возврате метода и уже обрабатывает это автоматически.