John Baum
John Baum

Reputation: 3331

Accessing a property inside a block

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)?

Upvotes: 1

Views: 1393

Answers (2)

Lyndsey Scott
Lyndsey Scott

Reputation: 37290

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.)

Upvotes: 1

CRD
CRD

Reputation: 53000

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.

Upvotes: 5

Related Questions