Without any preamble I want to show you problem I have in my program, I commented out steps and my thoughts for that steps. (I didn’t include @interface part for shortness, it has same method with the same signature as in @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;
}
How does an error object exist after it death? I understand that using NSError *__autoreleasing * will be right way to go, and situation will be trivial in that case, but how compiler reasoning for this code, where is my mistake in judgements?
It’s a bit an artificial question, but I can’t throw out this situation from my head, I think I’m loosing something.
Here is part of disassembly for -[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
If I understood correctly, there is only one object in this method, and it’s clearly released, not autoreleased or something else.
I suspect your confused about what’s getting released. I just checked the assembly output, and there is a call to
objc_release(), though I’m not familiar enough with x86 assembly to actually trace precisely what’s going on. However, I do know that the code here is expected to emit something of the equivalent of:and of course the optimizer will shrink that to
So I think you’re seeing the call to
objc_release()and thinking that your newly-allocated error is being released. It’s not. The previous value of*erroris being released before the newly-allocated error is placed in that location.