(NSError * __ strong *) магия - PullRequest
       5

(NSError * __ strong *) магия

1 голос
/ 25 января 2012

Без всякой преамбулы я хочу показать вам проблему, с которой я столкнулся в своей программе, я закомментировал шаги и свои мысли для этих шагов. (Я не включил @interface деталь для краткости, у нее тот же метод с той же сигнатурой, что и у @implementation)

@implementation Dummy

- (int)testing:(NSError *__strong *)error 
{
    *error = [[NSError alloc] initWithDomain:@"hello" code:42 userInfo:nil]; 
    // 3. retain count = 1

    // 4. because of ARC 'error' object was released for this time 
    // (assembly output is my proof) object is deallocated
    // retain count = 0

    return 0;
}

@end

int main()
{
    NSError *e = nil; // 1. retain count = 0 (obviously)
    Dummy *dummy = [[Dummy alloc] init];

    [dummy testing:&e]; // 2. passing reference to an error object

    // 'e' for this time has to be just a trash, or nil maybe, 
    // but next log gives me correct output: 
    NSLog(@"%@ %li", [e domain], [e code]); // 'hello 42'

    return 0;
}

Как существует объект ошибки после его смерти? Я понимаю, что использование NSError *__autoreleasing * будет правильным путем, и в этом случае ситуация будет тривиальной, но как рассуждения компилятора для этого кода, где моя ошибка в суждениях?

Это немного искусственный вопрос, но я не могу выбросить эту ситуацию из головы, я думаю, что что-то теряю.

Вот часть разборки для -[Dummy testing:]

callq  0x100000e8c <dyld_stub_objc_msgSend>
mov    -0x18(%rbp),%rcx
mov    (%rcx),%rdx
mov    %rax,(%rcx)
mov    %rdx,%rdi
callq  0x100000e92 <dyld_stub_objc_release>
mov    -0x24(%rbp),%eax
add    $0x40,%rsp
pop    %rbp
retq   

Если я правильно понял, в этом методе есть только один объект, и он явно освобожден, а не выпущен автоматически или что-то еще.

1 Ответ

3 голосов
/ 25 января 2012

Я подозреваю, что вы смущены тем, что выходит на свободу.Я только что проверил вывод ассемблера, и есть вызов objc_release(), хотя я недостаточно знаком со сборкой x86, чтобы на самом деле точно отслеживать, что происходит.Тем не менее, я делаю знаю, что код здесь, как ожидается, будет генерировать что-то вроде эквивалента:

NSError *temp = [[NSError alloc] initWithDomain:@"hello" code:42 userInfo:nil];
[*error release];
*error = [temp retain];
[temp release];

и, конечно, оптимизатор сократит это значение до

NSError *temp = ...
[*error release];
*error = temp;

Так что я думаю, что вы видите вызов objc_release() и думаете, что ваша недавно выделенная ошибка была выпущена.Это не.Предыдущее значение *error сбрасывается до того, как вновь размещенная ошибка будет помещена в это местоположение.

...