The exact value of the retain count is generally an implementation detail that depends on the compiler, the Objective-C language runtime, and any other libraries involved (like Foundation). But it's reasonable to wonder why it behaves this way, as long as you don't depend on the behavior.
Either your book is wrong, or you misunderstood it. Using __weak
doesn't put the object in the autoreleasepool.
Here is what is happening on line 32 (the one with the @"obj2: %ld"
format string).
To safely pass an object reference to a function or method (like NSLog
), it has to be a strongly-held reference. So the compiler generates a call to objc_loadWeakRetained
on obj2
. This function atomically increments the retain count of the object and returns the reference. Thus the retain count of the object goes from 1 to 2 before the object reference is passed to NSLog
.
Then, after NSLog
returns, the compiler generates a call to objc_release
. Thus the retain count goes back down from 2 to 1 by the third NSLog
.
If you want to be sure what's happening, look at the assembler output of the compiler. You can ask Xcode to show it to you:

Here's the relevant part of the assembly:
.loc 2 0 9 discriminator 1 ## /Users/mayoff/TestProjects/test/test/main.m:0:9
leaq -32(%rbp), %rdi
##DEBUG_VALUE: obj2 <- [%RBP+-32]
.loc 2 16 66 is_stmt 1 ## /Users/mayoff/TestProjects/test/test/main.m:16:66
callq _objc_loadWeakRetained
movq %rax, %rbx
Ltmp4:
.loc 2 16 29 is_stmt 0 ## /Users/mayoff/TestProjects/test/test/main.m:16:29
movq %rbx, %rdi
callq _CFGetRetainCount
movq %rax, %rcx
Ltmp5:
Ltmp20:
## BB#3:
##DEBUG_VALUE: obj2 <- [%RBP+-32]
##DEBUG_VALUE: obj1 <- %R15
Ltmp6:
.loc 2 16 9 discriminator 1 ## /Users/mayoff/TestProjects/test/test/main.m:16:9
leaq L__unnamed_cfstring_.4(%rip), %rdi
xorl %eax, %eax
movq %rcx, %rsi
callq _NSLog
Ltmp7:
Ltmp21:
## BB#4:
##DEBUG_VALUE: obj2 <- [%RBP+-32]
##DEBUG_VALUE: obj1 <- %R15
.loc 2 16 9 discriminator 2 ## /Users/mayoff/TestProjects/test/test/main.m:16:9
movq %rbx, %rdi
callq *_objc_release@GOTPCREL(%rip)
Line 16 is (in my test) the one that uses obj2
. You can see the call to objc_loadWeakRetained
before the call to CFGetRetainCount
, and the call to objc_release
after NSLog
returns.