Set delegates to nil under ARC? - ios

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.

Related

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.

Best practice for custom UIView subview strong vs weak

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.

iOS - UIViewController - Runtime error when overriding a getter in UIViewController subclass chain

iOS newbie here.
I have a class that is a subclass of UIViewController called CommonVC. It does not have a scene directly associated with it. It declares a property in the .h file:
#property (strong, nonatomic) SomeClass *someClass;
I modify one of my scene View Controllers to have CommonVC as the super class instead of UIViewContoller:
#interface SceneViewController : CommonVC
In SceneViewController, I create a lazy instantiation getter for the property:
- (SomeClass *)someClass
{
return self.someClass ? self.someClass :
(self.someClass = [[SomeClass alloc] initWithData:#"xxx"]);
}
This compiles OK, but crashes at runtime with EXC_BAD_ACCESS(code=2,...). The debugger is pointing at the override method and 'self' is nill (0x00000000).
The first reference to the getter (self.someClass) is in the viewDidLoad method. I would think that the view controller and its super class would have been instantiated at this point.
What is going on?
Thanks.
return self.someClass ? self.someClass
This is an endless loop. the code self.someClass is equivalent to [self someClass]; so that will get called endlessly. You will want to change your self.someClass to _someClass.
First of all, if you call self.someClass in your getter you end up in a loop as self.someClass it's actually calling that same method. This is probably the reason of your crash.
Secondly, every UIViewController instance always has a view (the scene you're talking about?) associated with it, hence all the -viewDidLoad, -viewWillAppear, etc. methods.
Lastly, it's very bad to have a 'common view controller' to handle stuff. You may want to use the delegation pattern or the singleton pattern instead.

Why is self.tableView nil when I (think I) set it up correctly?

I have added my custom UICollectionViewController as an object (A) to the interface builder, and given it the proper custom class.
this EOCollectionViewController is also an IBOutlet in my main Viewcontroller
I have added the UICollectionView to the main view of the application.
I have made all the links possible. UICollectionView has the object (A) as a delegate and a datasource.
The object (A) has the UICollectionView linked to the view property. (not the self.collectionView property, as this is not there)
It all works well. Collection view get's filled. The CollectionViewController resides in the property of my main view controller.
Only the self.collectionView is nil...when I try to reach it from within the UICollectionViewController.
My first guess is that "extra" UIControllers used in IB do not get initalized in the same way. I also tested, and viewDidLoad and init never get called for these extra objects.
Attached the header of EONoteController (and the IBOutlet added as a work-around)
#interface EONoteController : UICollectionViewController<UIGestureRecognizerDelegate>{
__weak IBOutlet UICollectionView *cvNotes;
}
#property (nonatomic) EDAMNotebook* notebook;
#property id <EODragHandler> draghandler;
#end
My first guess was to "fix" it, in the viewDidLoad, as follows
self.collectionView = self.view, but viewDidLoad never get's called.
What am i Missing?

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