that is a scheduling issue here, so let us just examine your code from perspective of time. I've added some timestamp marker to your code and let us walk through what really happens at those checkpoints.
- (void)hello: (void(^))callback {
// TIMESTAMP(A)
User *user = [[User alloc] init]; // user is NOT nil
// TIMESTAMP(B)
__weak User *weakUser = user;
// TIMESTAMP(C)
if(weakUser == nil) {
NSLog(@"nil1");
}
[ABC func: ^{
// TIMESTAMP(D)
if(weakUser == nil) {
NSLog(@"nil2");
}
callback();
}];
// TIMESTAMP(E)
}
we can agree in (A) < (B) < (C) < (E) and there are two more scenarios here
- (C) < (D) < (E) or
- (C) < (E) < (D)
there is no more information about which one of the two scenarios happens with (D).
(A)
you instantiate a __strong User * user
in this scope, the end of the scope is at (E) therefore your __strong
pointer will keep the instance alive in this context to the end of the scope. you have one retaining reference to your object.
(B)
you make a __weak User * weakUser
pointer, that points to a __strong
reference what you just established recently at (A). the __weak
pointers point to an instance as long as it is kept alive by a __strong
reference at least, but they are not retaining references – they become nil
immediately when no more __strong
reference points to the instance.
(C)
you still have a valid __strong
reference to your user
and the __weak
reference point to the same instance shamelessly, so the instance is still alive and the weakUser
is not a nil
, therefore the true-branch of if
will never run – nothing will be printed ever.
(E)
the scope runs out and the __strong
reference to your instance won't be live anymore, therefore it does not keep the user
alive after that point. additionally the weakUser
immediately becomes nil
, because that is not a retaining reference and mo more __strong
pointer keeps the user
alive.
scenarios for (D):
1. (C) < (D) < (E)
the block runs before the time (E) is reached, the __strong
pointer still keeps the instance alive and the weakUser
still points to the same instance, therefore the true-branch of if
won't perform.
2. (C) < (E) < (D)
the block runs after the time (E) is reached, the __strong
pointer is not live anymore, so the instance has been released by now and the weakUser
is nil
, therefore the true-branch of if
performs in this scenario.
NOTE: you can read more about encapsulation and ownership on the Apple's official site.