8

This code:

__weak VeryCool *weakSelf = self;
something.changeHandler = ^(NSUInteger newIndex) {
    if (newIndex == 0) {
        [weakSelf.options removeObjectForKey:@"seller"];
    }
};

gives me a warning that property options was not found. That's true, because options is an ivar, but not declared as property. Is it possible to somehow get options from weakSelf without declaring it as property?

0xSina
  • 20,973
  • 34
  • 136
  • 253
  • Why don't you want to use a property? – paulmelnikow Feb 12 '13 at 07:18
  • @noa I am using it now. I don't know why, i guess using ivar seems a lot clear syntax wise to me rather than declaring property (unless of course i need to expose the ivar to outside). – 0xSina Feb 12 '13 at 07:29
  • better duplicate: [Changing an instance variable in a block](http://stackoverflow.com/q/11752500) – jscs Feb 12 '13 at 08:04

2 Answers2

26

For direct ivar access, use ->. For example:

__weak VeryCool *weakSelf = self;
something.changeHandler = ^(NSUInteger newIndex) {
    if (newIndex == 0) {
        VeryCool* strongSelf = weakSelf;
        if (strongSelf)
            [strongSelf->options removeObjectForKey:@"seller"];
    }
};

It's important to check that strongSelf is non-nil because direct access to an instance variable will crash for a nil pointer (which is different from invoking methods with a nil receiver and property access is just method invocation).

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • 1
    wouldn't it cause retain cycle because strongSelf is not __weak? – 0xSina Feb 12 '13 at 00:11
  • 4
    You make the strong pointer within the block. The retain does not happen until the block runs and is released before the block completes. – Ken Thomases Feb 12 '13 at 00:12
  • I updated my answer so the code more directly corresponds to the code in the question and illustrates when the strong reference is taken. – Ken Thomases Feb 12 '13 at 00:17
  • I didn't down-vote, but the creation of a new `strongSelf` var is not needed. If you just check to see if `weakSelf` was not `nil`, and then use `weakSelf`. – Rob Feb 12 '13 at 00:29
  • 2
    weakSelf could become nil between the if(weakSelf == nil) and the use of it. Threading is hard. – Catfish_Man Feb 12 '13 at 00:30
  • @KenThomases Ok so you can't dereference a nil pointer because it will cause a crash...but doing [weakSomething hello] if weakSomething is nil won't cause a crash...why? Surely somewhere inside weakSomething is dereferenced and sent a 'hello' message and if weakSomething is nil, it will crash? Just curious – 0xSina Feb 12 '13 at 00:42
  • 4
    objc_msgSend takes care of messaging weak references safely (objc_msgSend-to-nil is safe, after all). What it doesn't take care of is making sure that multiple messages all go through. It's possible to write your code such that it behaves the way you expect even if any of N messages unexpectedly have a nil receiver, but it's generally easier to reason about if you ensure things are ok up front. – Catfish_Man Feb 12 '13 at 00:51
  • Exact solution I was looking for.. – ViruMax Feb 08 '17 at 13:05
3

It's not possible to dereference the weak pointer directly to get the ivar; trying to do so is a compiler error due to the race condition caused by the auto-nil behavior.

KVC will get the ivar for you, however:

[weakSelf valueForKey:@"options"]

This looks for an accessor method with the same name. If none is found, it will fall back on getting the ivar itself.

Since the reciever of the message valueForKey: is a weak reference, it will be nil if the object has been deallocated, making the message send a no-op. You thus avoid having to reassign self yet again in order to manually convince the AutomaticRC to do what you want.

jscs
  • 63,694
  • 13
  • 151
  • 195
  • hmm...is this a 'hack' or commonly used solution? Do you recommend just declaring it as a property instead of using KVO? – 0xSina Feb 12 '13 at 00:09
  • KVC has been part of Cocoa for a long time. I'd say this is a "neat" way around the fact that you can't dereference the `__weak` pointer; I'm not sure which would be better. – jscs Feb 12 '13 at 00:12
  • 1
    This is KVC, not KVO, and it gains you little-to-nothing over just accessing it directly. – Catfish_Man Feb 12 '13 at 00:12
  • @Catfish_Man: Right, mistyped (twice!). You can't access the ivar directly through the weak pointer. It's a compiler error: `Dereferencing a __weak pointer is not allowed due to possible null value caused by race condition, assign it to strong variable first` – jscs Feb 12 '13 at 00:12
  • Indeed! True of any use of a __weak pointer actually. The whole point of __weak is that it can become nil at any time, from any thread. The solution is a local strong reference. – Catfish_Man Feb 12 '13 at 00:14
  • @Catfish_Man: I find `valueForKey:` a _whole_ lot more straightforward than reassigning the same pointer twice to convince ARC to do what you want. – jscs Feb 12 '13 at 00:16
  • For a non-dereferencing-null example of why this can be tricky, consider: NSString *firstName = [weakPerson firstName]; NSString *lastName = [weakPerson lastName]; There's no way to guarantee that the condition ((firstName && lastName) || (!firstName && !lastName)) holds. – Catfish_Man Feb 12 '13 at 00:16
  • @Catfish_Man really?!? never knew that. – 0xSina Feb 12 '13 at 00:26
  • @0xSina: Those two operations can't be jointly atomic with respect to the nilling of `weakSelf` unless you could somehow get the same lock that the weak reference-tracking code (presumably) uses. In other words, there's no way to guarantee that the object won't be deallocated in between the two messages. – jscs Feb 12 '13 at 00:31