18

I turned on a new flag in xcode and get the warning "Weak receiver may be unpredictably null in ARC mode". This confuses me because OF COURSE it could be nil.

griotspeak
  • 13,022
  • 13
  • 43
  • 54

1 Answers1

27

I asked this question a week ago and received no answer, but Greg Parker answered it on the mailing list. So I am reposting with the answer.

We added this warning because we saw lots of subtle and hard to debug problems in practice.

The recommended practice is to read the weak variable into a strong local variable once, and then use the local variable.

  • Greg Parker

In my first incarnation of this question, I posted something like this, where I thought testing for nil should have been enough

if (self.rootViewController) {
    [self.rootViewController controllerWillChangeContent:controller];
}

The problem is that self.rootViewController could BECOME nill in the space between checking for nil and completing the method called. What we are told to do is to assign to a strong local reference and use that like so

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    RootViewController *rootVC = self.rootViewController;
    if (rootVC) {
        [rootVC controllerWillChangeContent:controller];
    }
}

Stephen Butler presented succinct restatement of the problem this warning is meant to combat

What we're trying to prevent is the object instance getting dealloced while you're in [someMethod] because you called it off a weak reference and nothing is holding onto the object strongly.

Community
  • 1
  • 1
griotspeak
  • 13,022
  • 13
  • 43
  • 54
  • It is a very debatable decision Apple took. To me it feels like warning a pointer can be null every time you dereference a pointer without an explicit check that it wasn't. But I can see why they did. People are used to work with non-volatile variables and not used to variables whose value may change at any time. Even your code is an example of not fully understanding the nature of weak references (the check for null is useless, you now understand it but didn't when you wrote the code). – Analog File Aug 10 '12 at 09:58
  • I believe this warning only shows up when you're *dereferencing* a pointer, doesn't it? So you wouldn't see it in method calls, but rather in things like `weakSelf->_someIvar`. Which makes sense, as dereferencing a null pointer can cause a crash, where messaging nil is just fine. – BJ Homer Aug 10 '12 at 13:19
  • 2
    It appears that simply assigning the weak reference to a strong ref is enough to eliminate the warning. The nil check in your second example above is unnecessary, as a message to nil is simply ignored. The only time the nil check is useful is if there's a lot of work that can be skipped if the result is going to be ignored or lost if the target is nil. – big_m Jun 15 '13 at 20:21
  • 2
    Further, it looks like the real point of the warning, according to [a follow-up](http://lists.apple.com/archives/objc-language/2012/Aug/msg00010.html) to the above quoted post, is less about catching messages to nil and more about preventing deallocation of an object *while it is servicing a message*. Keeping a strong reference for the life of the calling method prevents that from happening. – big_m Jun 15 '13 at 20:43
  • @big_m The check for nil is to protect against the case where you assign nil to the strong reference. While I admit that 'completing the method called' is a little open but that is what I meant. a weak reference could become nil at any time in between the check and completing the method (or in your words, 'servicing the message'). – griotspeak Jun 15 '13 at 22:18
  • @griotspeak, sorry, I missed that bit on first reading. Still, even if `rootVC` is nil, sending a message to it will not cause a runtime error; the message is just ignored, so the nil check is unnecessary here. You may _prefer_ it as a signal to a reader that you're expecting the reference could be nil sometimes, but it's important to be aware that such guards aren't really protecting you from anything. (Coming from Java, Ruby, C, etc., I found this a hard habit to break, and I still question the value of this feature, but there it is.) – big_m Jun 16 '13 at 13:23
  • @griotspeak, if I may add one more thing, I think it would make it more clear to also quote Stephen Butler's post I linked to above in your answer; Greg Parker's quote tells you _how_ to avoid the warning, but Stephen's explains _why_, which is something I like to see. Even though you did mention it, I found the message got lost between the examples, and I didn't clue in until I had read the entire thread you referenced (which, coincidently, was the second hit when I Googled the warning message, right after this post). – big_m Jun 16 '13 at 13:41
  • @big_m messaging nil is not an error but the return value from any message to nil is nil or 0. For myself, checking for nil is not really about avoiding the message to nil it is about avoiding using returns from nil. There is also the case where the object being nil is meaningful. All in all that if statement is not really the point but it *can* and often does protect against some things. – griotspeak Jun 16 '13 at 14:14
  • @griotspeak, fair enough, I agree that's an appropriate usage. However, it's not really relevant to your original question about how to avoid the warning about sending a message to a receiver held in a weak reference. I'm just suggesting that the answer could be improved by focusing on the point about holding a strong reference for sending messages and not so much on the nil check, which isn't really useful in the _specific_ example you used. (There's no return value, or it's ignored.) – big_m Jun 16 '13 at 16:09
  • 1
    Stephan Butler is wrong, if you directly call a method on a weak var, clang will retain it first, then call then method and finally release it again. When messaging a weak var, the object __cannot__ be deallocated before the call returns! However, if you message a weak var, then it is retained/released for every call and this is a huge performance penalty. See http://stackoverflow.com/a/15033758/15809 – Mecki Nov 04 '14 at 14:52
  • So, what's the idea to use "weak" in our properties, if at the end we will have to create a strong reference to prevent this warning? – Anel Rojas Hernández Jun 16 '15 at 20:58
  • @arh We need a strong reference for the duration of this work. If–before or after we initiate this work–all strong references are broken, we allow the object to be released and avoid a retain cycle. – griotspeak Jun 19 '15 at 23:25