1

I am new to blocks in iOS and had a quick question regarding their use. Say I have the following setup:

viewController.rowLabels = @[@"Hello", @"World"];
viewController.testBlock = ^(NSInteger itemIndex) {
// here i want to access another property of the viewController called foo
};

So, as seen above, I want to access another property of the view controller within the block itself. Do i need to do a *__weak -> strong assignment to achieve this or can i simply access it like NSLog(viewController.foo)?

John Baum
  • 3,183
  • 11
  • 42
  • 90

2 Answers2

5

The simple, but possibly problematic answer, is just to access it as you do the other properties:

viewController.rowLabels = @[@"Hello", @"World"];
viewController.testBlock = ^(NSInteger itemIndex) {
   ... viewController.foo ...
};

From your fragment we cannot know what viewController is - e.g. it could be a local variable from the method this fragment is in or a global variable etc. If you are just reading the value in viewController, as you are here, this does not matter[1].

The above works but there might be a problem: you probably have a strong reference cycle. The viewController instance references the block through it's testBlock property, and the block references the viewController instance. If both these references are strong (likely) then you have a circular dependency and the viewController instance and the block can never be freed by the system. You can break this cycle using a weak reference:

viewController.rowLabels = @[@"Hello", @"World"];
__weak ViewController *weakViewController = viewController; // make a weak reference to the instance
viewController.testBlock = ^(NSInteger itemIndex)
{
   // temporarily make a strong reference - will last just as long as the block
   // is executing once the block finishes executing the strong reference is
   // removed and no strong reference cycle is left.
   ViewController *myController = weakViewController;
   // only execute if the `ViewController still exists
   if (myController != nil)
   {
      ... myController.foo ...
   }
};

HTH

[1] note that the value you are reading is a reference to a ViewController instance and you can modify properties of that instance, what you cannot do (and are not trying to do) is modify which instance the viewController references if viewController is a local variable.

CRD
  • 52,522
  • 5
  • 70
  • 86
  • 1
    Note that automatically captured variables (non-__block variables, like your first example) are captured as const, and are thus immutable: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/TP40007502-CH6-SW1 – emma ray Dec 12 '14 at 20:17
  • @cdstamper - Exactly. But not the objects they reference. Updating using a block which object instance a local variable references is probably a more obscure scenario and I didn't want to muddy the waters any more in this question! ;-) – CRD Dec 12 '14 at 20:19
1

Declare a __block variable containing the view controller object, like so:

__block __weak ViewController *blockVC = viewController;
viewController.testBlock = ^(NSInteger itemIndex) {
     NSLog(@"%@", blockVC.foo);
};

I've used both __block and __weak since __block is implicitly strong, but adding __weak as a reference can help break the strong reference cycle while still using __block's strong reference to prevent deallocation.


As of iOS 5.0, it seems as if you can just create a __weak reference as opposed to using a __block variable to access a variable and its properties within a block:

__weak ViewController * weakVC = viewController;
viewController.testBlock = ^(NSInteger itemIndex) {

    ViewController * strongVC = weakVC;
    if (strongVC) {
        NSLog(@"%@", strongVC.foo);
    }
};

But note that unlike using __weak in combination with the __block storage type, __weak specifies a reference that might not keep the object alive, i.e. may deallocate, so even within the block, weakVC may be nil before you actually need it. (This is why the if (strongVC) conditional is required when just using a __weak variable alone.)

Lyndsey Scott
  • 37,080
  • 10
  • 92
  • 128
  • dear @furturereader. I hope you understand that the object in the block being nil dons mean to be an error, but just the calling scope being gone. That is just fine. don't force the scope to persist, as this is the retain cycle you want to avoid at any costs. If your code must contain such hacks it is the prove that your architecture is broken. – vikingosegundo Dec 13 '14 at 18:08
  • 2
    Unfortunately the statement "I've used both `__block` and `__weak` since `__block` is implicitly strong, but adding `__weak` as a reference can help break the *strong reference cycle* while still using `__block`'s strong reference to prevent deallocation." gives a completely wrong description of the semantics of `__block __weak` - this construct will **not** prevent deallocation. – CRD Dec 16 '14 at 18:17