Нужно больше объяснений по использованию autoreleasing
Я отчаянно пытаюсь понять использование __autoreleasing
ключевое слово Objective-C
. Я внимательно прочитал ответы на следующие вопросы:
в каких ситуациях нам нужно написать квалификатор владения _ _ autoreleasing под ARC?
использование _ _ autoreleasing в Примере фрагмента кода
и несмотря на то, что теперь я понимаю больше, я все еще не могу главное-цель. Зачем это делать? Позвольте объяснить, что именно меня смущает. Рассмотрим код:
@interface MyError : NSError
@end
@implementation MyError
- (void)dealloc
{
NSLog(@"My error dealloc");
}
@end
@interface ErrorHandler : NSObject
- (void)handleError:(MyError* __strong*)error;
@end
@implementation ErrorHandler
- (void)handleError:(MyError* __strong*)error
{
*error = [[MyError alloc] initWithDomain:@"Blabla" code:100500 userInfo:@{
NSLocalizedDescriptionKey : @"TestDescription"
}];
}
@end
- (void)test
{
MyError *error = nil;
ErrorHandler *handler = [ErrorHandler new];
[handler handleError:&error];
NSLog(@"Localized description %@", error.localizedDescription);
}
я написал этот код, чтобы увидеть, что произойдет, если я не использую __autoreleasing
. Как видите handleError
метод принимает ссылку на ссылку, которая явно объявлены как __strong
. И ничего не происходит. Все в порядке. Я могу получить информацию от MyError
объект, и он был успешно освобожден, я вижу это. Если я положу __autoreleasing
вместо __strong
ничего изменения. Так зачем использовать __autoreleasing
если это ничего не меняет? Вот чего я не понимаю. Кто-нибудь может показать мне, чего мне не хватает? Спасибо всем
1 ответов
я думаю, что ответ tl; dr заключается в том, что, объявив аргумент как __autoreleasing
вы также можете передать указатель на слабую ссылку, если вы хотите.
представьте, что ваш метод выглядит так:
-(void) handleError: (NSError* __strong *) error
{
NSError* myError = [[NSError alloc] init];
*error = myError;
}
компилятор думает, что *error
является сильным, поэтому, когда он выполняет задание, вы получаете +1 сохранить количество. После того, как компилятор был над кодом, он выглядит следующим образом:
-(void) handleError: (NSError* __strong *) error
{
NSError* myError = [[NSError alloc] init];
*error = [myError retain];
[myError release];
}
так что если вы называете это так:
NSError* error; // strong reference
[self handleError: &error];
это отлично, потому что компилятор правильно поместит выпуск в конце области. Если вы сделаете это, хотя:
NSError* __weak error; // weak reference
[self handleError: &error];
вероятно, он не будет компилироваться, но если это так, потому что компилятор считает ссылку слабой (она не может видеть сильное назначение в handleError:
), оно не положит в отпуск и предмет протекет.
если вы определяете метод следующим образом:
-(void) handleError: (NSError* __weak *) error
{
NSError* myError = [[NSError alloc] init];
*error = myError;
}
компилятор добавляет код, чтобы он выглядел так это:
-(void) handleError: (NSError* __weak *) error
{
NSError* myError = [[NSError alloc] init];
*error = myError;
[myError release];
}
это еще хуже, потому что задание *error
дает +0 сохранить счет, что означает, что, как только myError
выходит за рамки, т. е. когда метод возвращается, объект, на который он указывает, освобождается.
если вы сделаете это:
-(void) handleError: (NSError* __autoreleasing *) error
{
NSError* myError = [[NSError alloc] init];
*error = myError;
}
компилятор делает это:
-(void) handleError: (NSError* __autoreleasing *) error
{
NSError* myError = [[NSError alloc] init];
*error = [[myError retain] autorelease];
[myError release];
}
что совпадает с предыдущим случаем, за исключением того, что возвращаемый объект находится в пуле autorelease в конце метода, поэтому он не получит освободившему. Таким образом, вы можете объявить error
в вызывающем абоненте как сильный или слабый, и компилятор имеет возможность сделать что-то разумное с возвращаемой ссылкой.
по крайней мере, я думаю, что это проблема.