Best practice for custom UIView subview strong vs weak - ios

So I am writing a custom class that inherits from UIView. I have a bunch of subviews, that I add.
So run into 2 issues. IF I make the superview and subview references strong, the views are leaking. If I make them weak, they are not appearing at all. What am I doing wrong?
CustomUIView
#interface CustomUIView : UIView
#property(nonatomic, strong) AnotherCustomUIView *mySubView;
#end
#implementation CustomUIView {
- (void)initCommon {
self.mySubView = [self createSubView]
}
- (AnotherCustomUIView *) createSubView {
return [[AnotherCustomUIView alloc] init:self];
}
#end
AnotherCustomUIView
#interface AnotherCustomUIView : UIScrollView
#property (nonatomic, strong) CustomUIView *ownerView;
#end
#implementation AnotherCustomUIView
- (id)init:(CustomUIView *) ownerView {
self = [super init];
if (self) {
self.ownerView = ownerView;
self.delegate = self;
}
return self;
}
#end

Based on your code, I think you have a confusion between strong and weak references, and how those relate to memory management.
First, here is a great description of strong vs. weak: https://stackoverflow.com/a/11013715/700471
In your specific case, the ownerView property on AnotherCustomUIView should be weak as it is pointing to a senior object. Delegate references are also weak, I don't know that setting self.delegate = self; has any negative impact.
Edit:
To clarify, adding a view as a subview of another view creates a strong reference to it. Further strong references should not be necessary once your view is part of the view hierarchy. So you have this code:
[mySeniorView addSubView:myJuniorView]; // myJuniorView will not be released.
mySeniorView.juniorView = myJuniorView; // This shouldn't be a strong reference.
In the above code, if mySeniorView.juniorView is a strong reference, it's redundant. If you remove myJuniorView from the view hierarchy it won't be deallocated because you still have another strong reference to it. If .juniorView is a weak reference, then removing myJuniorView from the view hierarchy will cause it to deallocate and set .juniorView to nil.
That's why, for example, all of your IBOutlet properties should be weak; because the things in your xib that you're connecting them to are part of the view hierarchy and therefore will not dealloc because their senior views have strong references to them.
So, while I pointed out that your junior object should not have a strong reference to your senior object (neither one will deallocate; this is called a retain cycle and results in memory leaks) probably your senior object shouldn't have a strong reference to your junior one, unless you want it to hang around even when it's not in the view hierarchy. These are views, after all; I would say create your junior object, pop it into the view hierarchy and then save a reference to it in a weak property so you can access it for as long as it exists. Then, when you remove it from the view hierarchy, your reference nils out.
Anyway, hope that helps.
Reference: Apple Docs, addSubview:

Almost always the reference to the parent should be weak. This is typical chain of command. The parent creates and "owns" the child, and the child merely observes its parent. You want the child to be existentially dependent upon the parent, not the other way around.

Related

iOS Viewcontroller with reference to table view IBOutlet - Weak or Strong?

As I read in another question, a rule of thumb was:
When a parent has a reference to a child object, you should use a strong reference. When a child has a reference to its parent object, you should use a weak reference or a unsafe_unretained one (if the former is not available). A typical scenario is when you deal with delegates. For example, a UITableViewDelegate doesn't retain a controller class that contains a table view.
Well, in this case I have a view controller with a table view IBOutlet. This table view also uses the view controller as a delegate and data source. By the rule above, the view controller acts as the parent and so I should have a strong reference to the table view - the table view should have a weak reference to the view controller.
I'm not sure how to define that latter weak reference - I'm setting
tableView.delegate = self;
tableView.dataSource = self;
How am I supposed to make myself weak? Or is this not the right way to do it?
You can just drag the outlet from your UITableView to your .h file. Xcode will create appropriate property for you. You can choose weak/strong type from there as well while naming the Outlet.
If you would however like to create it manually, you can add this line to your .h file:
#property (weak, nonatomic) IBOutlet UITableView *tblView;
But do keep in mind that even after adding this line to your .h. file, you will still have to connect it to your UITableView.
Also don't forget to make your ViewController conform to UITableViewDelegate like below:
#interface MyTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
In your title however, you seem to be confused about the difference between Weak and Strong properties. Ole Begemann has answered about the difference splendidly on This question.
The code you wrote is fine. You don't have to make yourself weak. The delegate and datasource properties are declare as weak in the UITableView definition.
delegate and datasource are already weak as per UITableView.h.
#property (nonatomic, weak, nullable) id <UITableViewDataSource> dataSource;
#property (nonatomic, weak, nullable) id <UITableViewDelegate> delegate;
If any instance is set as delegate and datasource won't get retained by tableview instance
And when you make a IBOutlet make it like this. Choose weak if its strong by default its weak as shown in image
#property (weak, nonatomic) IBOutlet UITableView *tableview;
As the view is already retained by the view it's added (i.e self.view)
It is just like a view in UIKit Control to display data in list form. It is not a child controller. All UIKit items are referenced as weak.

Pass scrollview delegate to child view controller

I cant seem to figure out how to set a scrollview's delegate as a viewcontroller within the scrollview.
ChildView *child = [[ChildView alloc] init];
_scrollview.delegate = child;
My child view controller is using the scrollview delegate:
#interface ChildView : UIViewController <UIScrollViewDelegate>
But it won't call scrollViewDidScroll;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
NSLog(#"%f", scrollView.contentOffset.y);
}
Basically, I would like the scrollview offset of a scrollview to be passed to a view controller nested within it.
There are several ways you can assign a property in Objective C.
Assign means to simply performs a variable assignment and sets one variable equal to another. You won't be able to use the variable if it goes out of scope. This is typically only used for C primitives.
Weak tells the compiler to keep a reference to the object as long as someone else points to it strongly.
Strong tells the compiler to keep a strong reference to the given object. This means that the object won't go out of scope until the object holding it goes out of scope.
As #Aaron pointed out, delegates properties are set using assign. This means that if the variable you are setting the delegate to goes out of scope, the reference becomes invalid. To remedy this, you have to keep a strong pointer to the object around so that it does not go out of scope.
To do this, create a property in your main view controller:
#property (strong, nonatomic) ChildView * child;
Then, when you create the child view, set the property equal to it.
self.child = ChildView *child = [[ChildView alloc] init];
Now you can set the delegate and it won't go out of scope once your function terminates.
_scrollview.delegate = self.child;
child is getting deallocated by the time the delegate method fires.
From UIScrollView.h:
#property(nonatomic,assign) id<UIScrollViewDelegate> delegate; // default nil. weak reference
This is a weak reference, so it won't be retained. To fix it, create a strong #property in your main view controller, and set it to child.

Passing object from child to parent

My problem is that I want to give a object back from child view (controller) to a parent view (controller). I pass the object with a call like this:
parentView.someObject=objectFromChild;
Everything is okay until the child view gets deleted (because it is poped up and no pointer shows on it) but then the object passed from child view to parent view gets also deleted. Can anyone tell me how to make it possible to save my object(even if the view which created it, is deleted)? With NSString my method works very well...but with objects I always get EXC_BAD_ACCESS
Make sure the parent object is retaining it.
Sounds like there are a couple of challenges here and I'll try to give some simple tips.
Passing Data
If you want to pass an object upward from a child to a parent, design your Child class so that the object or variable is a public property. Then, any other object (like Parent objects that own the Child) can access that property.
Keeping Data Alive
Usually EXC_BAD_ACCESS means the object has already been deleted by the system. Tell the system you want to hang on to the object by setting 'strong' in the property declaration, and this will take care of your EXC_BAD_ACCESS problem.
Take a look at the following code for an example of how to implement a very simple parent/child data relationship and retain data.
//****** Child.h
#interface Child : NSObject
// Child has a public property
// the 'strong' type qualifier will ensure it gets retained always
// It's public by default if you declare it in .h like so:
#property (strong, nonatomic) NSString *childString;
#end
//****** ParentViewController.h
#import <UIKit/UIKit.h>
#import "Child.h"
#interface ParentViewController : UIViewController
#property (strong, nonatomic) Child *myChild;
#end
//****** ParentViewController.m
#implementation ParentViewController
#synthesize myChild;
- (void)viewDidLoad {
[super viewDidLoad];
// create child object
self.myChild = [[Child alloc] init];
// set child object (from parent in this example)
// You might do your owh setting in Child's init method
self.myChild.childString = #"Hello";
// you can access your childs public property
NSLog(#"Child string = %#", self.myChild.childString;
}

Set delegates to nil under ARC?

I'm writing iOS apps using ARC and targeting iOS 5+.
Suppose I write a custom view object that has a delegate property. In declaring the delegate property, I make it a weak reference to avoid a retain cycle, so that when the actual delegate object (the controller) is destroyed, my custom view will also be destroyed, as follows:
#interface MyCustomView : UIView
#property (nonatomic, weak) id<MyCustomViewDelegate> delegate;
#end
All is good.
Ok, so now I'm writing the controller object, and it has references to two view objects: my custom view and an Apple-supplied UIKit view, both of which declare delegate properties, and the controller is the delegate for both views. Maybe it looks something like this:
#interface MyViewController : UIViewController <MyCustomViewDelegate, UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, strong) MyCustomView *customView;
#property (nonatomic, strong) UITableView *tableView;
#end
#implementation MyViewController
- (void)viewDidLoad
{
self.customView.delegate = self;
self.tableView.dataSource = self;
self.tableView.delegate = self;
}
#end
My question is this: Do I need to override dealloc to set either or both delegates to nil?
I mean, as I understand it, the delegate property of the UIKit view (in this case, tableView) isn't actually declared to be a weak reference, but rather an __unsafe_unretained reference, for backwards compatibility with non-ARC version of iOS. So maybe I need to write
- (void)dealloc
{
_tableView.dataSource = nil;
_tableView.delegate = nil;
}
Now, if I do have to override dealloc, I still don't have to set _customView.delegate = nil, right? Because that was declared (by me) to be a weak reference, so it should be set to nil automatically upon the destruction of MyViewController.
But on the other hand, I'm not targeting non-ARC versions of iOS, nor do I intend to. So maybe I don't need to override dealloc at all?
Setting non-weak delegates to nil is generally a good idea unless you know you don't have to. For UITableView and UIScrollView, I've experienced crashes on previous iOS versions with the following steps (it may help to run with zombies enabled):
Scroll really fast.
Press Done or the back button or whatever to dismiss the VC.
This appears to happen because the scrolling animation is retaining a reference to the view, so the view outlives the VC. It crashes when sending the scroll event.
I've also seen crashes after dismissing a VC containing a UIWebView while a request is being loaded, where simply setting the delegate to nil was not sufficient (I think the workaround was to call [webView loadRequest:nil]).
If the only strong reference to said tableView is your sole MyViewController controller, you don't need to manually set UITableViewDelegate or UITableViewDataSource to nil.
The reason is that once the dealloc method on your MyViewController is called, the tableview will also be destroyed along with the controller (that is, once again, as long as the only reference to it is your sole controller MyViewController class).
If you have other strong references to this tableview, such as other controllers, it would then be possible that the tableview could then exist longer than the MyViewController class. In such a case, it would be necessary to set the UITableViewDelegate and UITableViewDataSource to nil in the dealloc method of MyViewController because, as you mentioned, these properties are NOT weak references and will not automatically be set to nil.
However, this sort of situation is pretty rare in my experience though.
Most of the time, I don't worry about setting these to nil honestly, but it is a defensive programming practice.
See this post also:
In dealloc method set any delegate to nil is needed or not needed
The only reason you would want to explicitly set the delegate and dataSource to nil is if the customView or the tableView could out live the view controller. Setting them to nil would guard against the delegate or dataSource referencing a deallocated object.
If the customView and tableView will be deallocated along with the view controller, there is no need to nil out the delegate and dataSource.

Memory management, things to be clear

I need to get things clear about Objective-C memory management:
If I declare an object in the class header as ivar without #property:
#interface MyFacebooDelegate : UIViewController
{
TableViewController *tableController;
}
...
#end
and some where in the code for example in - (void)viewDidLoad I do :
tableController = [[TableViewController alloc] init];
so where is best way to release it. What if I make the instant object a property what will be the different? and how the memory management will be too
#interface MyFacebooDelegate : UIViewController
{
TableViewController *tableController;
}
...
#end
#property (nonatomic, strong) TableViewController *tableController;
What the following syntax do exactly for the object viewController:
.h
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) ViewController *viewController;
#end
.m
#implementation AppDelegate
#synthesize window = _window;
#synthesize viewController = _viewController;
- (void)dealloc
{
[_window release];
[_viewController release];
[super dealloc];
}
.....
#end
If I want to return an object through a method to another class, do I need to autorelease it in the method body first and then retain it in receiver side?
for example this method what exactly to do in the method body and in the receiver side too:
-(NSString *)getFriendId
{
NSArray *ar = [NSArray arrayWithObjects:#"1",#"2",#"3", nil];
return [ar objectAtIndex:0];
}
I know this a lot but I am really confused and need your help.
1) best way is in dealloc; or right before re-setting it.
2) a property does the retain/release for you. But WARNING! You keep mixing up things. You use "strong" here, which relates to ARC. If you really insist on using classic retain/release (you shouldn't) then use (nonatomic, retain) instead.
3) Your properties get deallocated on dealloc. Again, strong is wrong here.
4) Yes. Ideally you should. Another reason why ARC is awesome, it does this all for you, automatically.
tl;dr: Use ARC. Never go back. (But still learn manual memory management)
ARC is the answer for your all memory management question. Very import note on Strong and Weak property in addition to ,
iOS Strong property: So strong is the same as retain in a property declaration before ARC. For ARC projects I would use strong instead of retain, I would use assign for C primitive properties.
iOS outlets should be defined as declared properties. Outlets should generally be weak, except for those from File’s Owner to top-level objects in a nib file (or, in iOS, a storyboard scene) which should be strong. Outlets that you create will therefore typically be weak by default, because: Outlets that you create to, for example, subviews of a view controller’s view or a window controller’s window, are arbitrary references between objects that do not imply ownership.

Resources