Big Nerd Ranch, chapter 19, Avoiding strong reference cycle by reassigning pointers - ios

I am going through Big Nerd Ranch iOS textbook, 19th chapter and it is unclear for me why they are doing reassignment of pointers there to avoid strong reference cycle. Basically they have a block of code, actionBlock that has a strong reference to the class, BNRItemCell, and the reference persists beyond the life of the block. BNRItemCell is referencing the block through #property: #property (strong, nonatomic) void (^actionBlock)(void). Obviously that creates strong reference cycle. To avoid it they define a __weak BNRItemCell *weakCell = cell outside of the block, and then BNRItemCell *strongCell = weakCell inside the block. They say that strongCell should persist while the block is executing and that it will be destroyed when the block is finished. I do not understand the reassignment within the block, BNRItemCell *strongCell = weakCell, and what is the point of the pointer being __weak. If, say, we do just BNRItemCell *strongCell = cell, where cell is the strong reference to the class, shouldn't it be OK? strongCell would still be destroyed when the block has finished executing.
I tried to visualize what is going on and what they are doing does not make sense for me. To make things clear, what is the difference between
__weak someClass *weakPointer = strongPointer_1;
someObject.actionBlock = ^{
someClass *strongPointer_2 = weakPoiter;
// Here we are using strongPointer_2
}
and
someObject.actionBlock = ^{
someClass *strongPointer_2 = strongPointer_1;
// Here we are using strongPointer_2
}
?
I just can not get the difference, so I guess, I do not have clear understanding of what is going on behind the scenes.
The full code is below:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Get a new or recycled cell
BNRItemCell *cell =
[tableView dequeueReusableCellWithIdentifier:#"BNRItemCell"
forIndexPath:indexPath];
// Set the text on the cell with the description of the item
// that is the nth index of items, where n = row this cell
// will appear in on the tableview
NSArray *items = [[BNRItemStore sharedStore] allItems];
BNRItem *item = items[indexPath.row];
// Configure the cell with the BNRItem
cell.nameLabel.text = item.itemName;
cell.serialNumberLabel.text = item.serialNumber;
cell.valueLabel.text = [NSString stringWithFormat:#"$%d", item.valueInDollars];
cell.thumbnailView.image = item.thumbnail;
__weak BNRItemCell *weakCell = cell;
cell.actionBlock = ^{
NSLog(#"Going to show image for %#", item);
BNRItemCell *strongCell = weakCell;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad){
NSString *itemKey = item.itemKey;
// if there is no image, we don't need to display anything
UIImage *img = [[BNRImageStore sharedStore] imageForKey:itemKey];
if (!img) {
return;
}
BNRImageViewController *ivc = [[BNRImageViewController alloc] init];
ivc.image = img;
ivc.modalPresentationStyle = UIModalPresentationPopover;
ivc.preferredContentSize = CGSizeMake(380, 300);
CGRect frame = [self.view convertRect:strongCell.thumbnailView.bounds
toView:self.view];
// frame.origin.y -= 150;
UIPopoverPresentationController *popoverController = ivc.popoverPresentationController;
popoverController.permittedArrowDirections = UIPopoverArrowDirectionUp;
popoverController.sourceView = cell.thumbnailView;
popoverController.sourceRect = frame;
[self.navigationController presentViewController:ivc animated:YES completion:nil];
}
};
return cell;
}

It's important to understand why the "normal" case of capturing self or some other reference in a block causes a retain cycle.
If you have an instance variable holding a strong reference to a block, that block won't be dealloc'd until that reference is niled. If the block captures a strong reference to the instance which references the block, that instance won't be dealloced until that reference is niled. If both the instance and the block hold these references at the same time, you have your classic loop, unless one explicitly nils its reference to the other.
The key thing here is that the block's reference to the class instance is held in the capture data. This data sticks around as long as the block does.
Now we can see why capturing a weak pointer will help. If the problem is the capture data, and not the references in the block itself, a weak pointer takes care of our cycle rather nicely. The class instance can now be dealloced independently of the block, because the block has no strong reference.
Now the question is, why have a strong reference within the body of the block itself? This is simply to make sure the instance won't be dealloced while the block is executing. This isn't a real concern for stuff that is all running on the main thread, but that block might be dispatching to a background thread.
Why doesn't this (re)create a retain cycle? Well, it does, but only temporarily. The strong reference inside the block captures the weak pointer, which can be niled. The strong reference is a block-scoped variable that will be cleaned up when the block exits. ARC ensures that such references are released before the return from the block, thereby stopping the cycle.

If I'm understanding this correctly, it looks like they are trying to prevent a retain cycle.
__weak BNRItemCell *weakCell = cell;
cell.actionBlock = ^{
BNRItemCell *strongCell = weakCell; // weakCell is captured here at block declaration time
}
Blocks capture and retain variable values, so if it captured a strong BNRItemCell (reference to itself), its strongly retained actionBlock would create a retain cycle. By capturing the BNRItemCell as weak, if the table view ever decided to discard the cell, your cell will be properly deallocated.
If it helps you, think about how many strong references exist to the BNRItemCell. The table view keeps a strong reference (+1) while the cell is on screen. By capturing a strong reference (+1) in the actionBlock, you'd have brought its reference count up to 2.
An object will remain in memory as long as its reference count is 1 or more. So even if the UITableView releases its reference to the cell, you've still got the reference in its actionBlock, which will keep its reference count positive. Since you never set actionBlock = nil (which you shouldn't), actionBlock will never get released, and therefore the BNRItemCell never will either.
Note that weakCell is the only captured value at block creation time, strongCell won't even exist until the block is actually called.
TL;DR: The cell would be keeping a strong reference to itself through its actionBlock, which would create a retain cycle.

Related

Xcode Memory Debugger showing instances of already removed objects - iOS

Please see attached image for reference. All of the viewControllers have been already removed from the app but Memory Debugger shows its instances as well as all its properties. When i click the Show Only Leaked blocks filter of Memory Debugger, the viewControllers and other instances do not appear in it. Does it mean there are no leaks?
How do I solve the problem. What does it mean?
I do have the PKYStepper block in the CartViewController's cellForRowAtIndexPath (Stepper is a UIControl in my TableViewCell) method as follows :
PKYStepper *qtyStepper = [cell viewWithTag:993];
qtyStepper.tappedCallback = ^(PKYStepper *stepper) {
NSLog(#"Tapped!");
rowSelected = indexPath;
if (((Dish*)((MenuSubSection*)_section.subSections[0]).dishesArray[indexPath.row]).disheOptions.count)
{
UIWindow *window = [UIApplication sharedApplication].keyWindow;
NSBundle* bun = [NSBundle bundleWithIdentifier:#"com.test.test"];
DishItemOption *dishOptions = [[bun loadNibNamed:#"DishItemOption" owner:self options:nil] objectAtIndex:0];
dishOptions.frame = CGRectMake(0, 0, window.frame.size.width, window.frame.size.height);
dishOptions.dish = [[Dish alloc] initWithDishObject:((Dish*)((MenuSubSection*)_section.subSections[0]).dishesArray[indexPath.row])];
dishOptions.delegate = self;
[window addSubview:dishOptions];
}
};
How to make it reference the Weak Self?
It looks like you've likely captured the view controller in a callback block of some sort.
Specifically, PKYStepper seems to have a callback block that is strongly referencing the view controller. Either make sure said reference is weak or make sure the block is properly destroyed when the view controller is torn down.
Found the solution. Updated my callbacks to the following :
__weak typeof(self) weakSelf = self;
qtyStepper.incrementCallback = ^(PKYStepper *stepper, float newValue) {
CartViewController *sSelf = weakSelf;
[sSelf updateTotalCharges]; //Had to use WEAKSELF in the callback!
};

Preventing `self` from creating a strong reference in blocks

With the recent XCode update some code blocks are displaying as warnings where "Block implicitly retains 'self'"
It is my understanding that the when you create blocks it is best practice to create a weak self to keep from creating a strong reference that will not be garbage collected.
In the below example I set the myArray to self->myArray as recommended by XCode. Does this create the strong reference? Why can't I use 'weakSelf->myArray`? Attempting to do so results in this error:
Dereferencing a __weak pointer is not allowed due to possible null
value caused by race condition, assign it to strong variable first
I thought the whole point was to create weak refrences? Isn't weakSelf just a pointer to self?
Is the self-> even necessary in the below instance?
#interface SomeViewController (){
NSMutableArray * myArray;
}
#end
- (void) doSomethingInBackground {
// Do NSURLSessionTask on the background and onCompletion call mySuccessBlock.
}
- (SomeBlock) mySuccessBlock {
__block __typeof__(SomeViewController) __weak * weakSelf = self;
return ^(NSDictionary* result){
//this line is my related to my question
self->myArray = [weakSelf sortResultsAlphabetically: result];
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.tableView reloadData]
});
};
}
Would recasting to be the correct way?
SomeViewController * strongSelf = weakSelf;
strongSelf->myArray = [weakSelf sortResultsAlphabetically: result];
The error message is right. You have to do the "weak-strong dance". You are only doing half of the dance. Pass self into the block as weak, but then immediately assign it, inside the block, to a strong reference (as in your edited "Would recasting to be the correct way?").

Weak self vs self when using closure defined in swift in objective c

Let say :-
I have a ViewController created in objeticve - c (Number.m).
I have a TableViewCell created in swift which contains button (CalculateSum.swift)
Now i need to tell VC that button has been tapped and send tag of the button. For this i use closure.
Swift :-
var calculateSumBtnTapped: ((UITableViewCell) -> Void)?
#IBAction func calculateSumBtnDidTapped(_ sender: Any) {
self.calculateSumBtnTapped?(self)
}
Now as we see CalculateSum.swift holds strong reference to calculateSumBtnTapped property.
Now i call this closure as block in Number.m
CalculateSum * calculateSum = [tableView dequeueReusableCellWithIdentifier:#"sum" forIndexPath:indexPath];
__block typeof(self) weakSelf = self;
calculateSum.calculateSumBtnTapped = ^(UITableViewCell * _Nonnull calculateSumCell) {
// calculating sum by tag
weakSelf.sum = weakSelf.sum + calculateSumCell.tag
};
Is it necessary to use self as weakself ? or I can use self as it is ?
First of all try to understand what is a retaincycle and how it will affect your application..
A retain cycle is a condition that happens when two objects keeps strong reference to each others.
In such cases these objects won't get deallocated and it will stay in memory forever and leads to memory leak.
Retain cycle in blocks and why should we use weakself
Closures and blocks are independent memory objects and they will retain the objects they reference so if we are accessing any class variable or method inside the closure or block using self then there is a chance for retain cycle
self.myBlock = ^{ self.someProperty = xyz; }; // RETAIN CYCLE
will get this warning
Capturing 'self' strongly in this block is likely to lead to a retain
cycle
To avoid such situation we should weakSelf to access members
__weak typeof(self) weakSelf = self;
`self.myBlock = ^{ weakSelf.someProperty = xyz; };`
So there is a rule like always use weakSelf in blocks but there are some special cases like animation blocks
[UIView animateWithDuration:duration animations:^{ [self.superview layoutIfNeeded]; }];
Here we can use self inside the block because the blocks get destroyed automatically once animation completed.
When an object A reference another object B strongly and object B references object A strongly, ARC cant dealloc there two objects thus creating retain cycle which may increase the memory footprint of your app and may cause app to crash. To avoid retain cycle one of the reference should be weak. Thats why you need to use weakSelf inside the block.

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

When and why would I want to declare a local variable as __weak using ARC?

Mike Ash has written this introduction to ARC where he introduces something like:
__weak Foo *_weakFoo = [object foo];
Why would I want to do that for a local, temporary variable? __weak is a zeroing reference which will set the _weakFoo pointer automatically to nil as soon as the referenced object gets deallocated. Also, __weak is only available in iOS >= 5.
When would I run into trouble when I simply do this?:
Foo *_weakFoo = [object foo];
This is always expected to return an object or nil. My guess is this:
Foo *_weakFoo = [object foo];
[self doSomethingStupid]; // does something bad so foo gets deallocated
[_weakFoo doIt]; // CRASH! msg sent to deallocated instance 0x123456
One thing that still bugs me with ARC is: When does it know that I don't need an object anymore? I'd argue that when I set a pointer to nil or to something else it figures out that the previously referenced object isn't needed by this owner anymore and therefore maybe can go away. But the point is: I set it to nil. So it's nil anyways!
So when would __weak for a local variable make sense, and what kind of crazy thing must I do somewhere else so that I really need that?
I use __weak local variables if I have to manipulate self inside of a block to avoid a retain cycle. Consider this example where I'm using GCD and blocks to perform a network request for a string, and then setting it on a label declared by the class, in this case, TurtlesViewController.
__weak TurtlesViewController *weakSelf = self;
dispatch_queue_t networkQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(networkQueue, ^{
// Kick off a network task for some data that is going to populate a label declared on our class
NSString *returnString = [networkDataSource retrieveTurtleTime];
// Dispatch back to the main thread to populate the UILabel
dispatch_async(dispatch_get_main_queue(), ^{
// Using self.label here creates a retain cycle. Self owns the block and the block has captured self
self.label.text = returnString;
// Instead, we use weakSelf for our reference to the label as it will be torn down by ARC at the end of the loop.
weakSelf.label.text = returnString;
});
});

Resources