Reputation: 3331
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
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
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