iOS: Does an UIView hold an reference to its UIViewController? - ios

By default, UIView returns its UIViewController in nextResponder function - if it has one. I think UIView must have hold an reference to the UIViewController.
If so, won't it lead to a circular reference between them, since UIViewController also holds an reference to UIView?
--- update -----------
I notice that the UIView has an private member named _viewDelegate, which is the UIViewController. Anyone knows how this is assigned?
--- update -----------
I found that when the view is loaded and assigned to the view controller, its _viewDelegate property points to the view controller at the same time. So this probably happens in the didSet event of view property.
According to the name of property _viewDelegate, it should be a delegate, which normally is a weak reference, I guess.

Does an UIView hold an reference to its UIViewController?
A UIView might or might not hold a reference to a UIViewController, but this is an internal implementation detail. It is not documented, and should not be relied upon.
If so, won't it lead to a circular reference between them, since UIViewController also holds an reference to UIView?
This is a circular reference in the simplest sense, but that is not necessarily a strong reference cycle or retain cycle. That is, the internal reference could be weak or unsafe_unretained or manually set to nil at the appropriate time.
There might not always be a stored reference, though. Code like this can traverse the responder chain and find the closest view controller:
// self is the view
UIResponder *responder = self;
while ((responder = [responder nextResponder])) {
if ([responder isKindOfClass:UIViewController.class]) break;
}
return (UIViewController *)responder;
(Code from UIView+PSPDFKitAdditions.m.)
The references you're seeing might be loaded lazily, or just be temporary cached values.

Related

Would it cause retain cycle that add a subview on a view passed by parameter of a function?

For example:
// CustomViewClass
- (void)showOnView: (UIView*)view {
[view addSubview: self.customView];
}
Then invoke this method at another class, like a view controller's viewDidLoad.
- (void)viewDidLoad {
[super viewDidLoad];
[self.customViewClass showOnView: self.view];
}
I pass a view controller's view to CustomViewClass as the above.
My question is: Would it cause some kind of retain cycle?
Are these views passed through parameter referenced weakly?
Or it's fine to that.
It would be highly appreciated if anyone explain it in both Swift and Objective-C.
What is Retain Cycle? - It's the condition when 2 objects keep a reference to each other and are retained, it creates a retain cycle since both objects try to retain each other, making it impossible to release.
In this case, self.customViewClass keep a reference to self.view but self.view doesn't keep any reference to self.customViewClass. Which keeps reference to self.customViewClass is self, not self.view.
So of course, it won't causes retain cycle
Don't believe in me ? đŸ¤£ - Check it yourself by trying to log something inside dealloc method.
After you dismiss CustomViewController, if the code inside dealloc is called and log something, it means no retain cycle here. If not, it causes retain cycle.
For example
- (void)dealloc {
NSLog(#"BOOM RIGHT ANSWER!!!");
}
Memory leaks happens when two class having objects pointing to each other .For e.g.
class A{
var object_b = B()
}
class B{
var object_a = A()
}
Now consider your case :
// CustomViewClass
- (void)showOnView: (UIView*)view {
[view addSubview: self.customView];
}
Your" view" object is local variable .CustomViewClass doest not reference to superview "view".
Now when customview is added to superview:
- (void)viewDidLoad {
[super viewDidLoad];
[self.customViewClass showOnView: self.view];
}
When showOnView function of CustomViewClass called the superview simply adds subview CustomViewClass view.
For finding memory leaks always add deinit function in views and viewController class so that you can sure about class is deallocated or not.
deinit {
print("deinit called " + "Class name")
}
This won't lead to a retain cycle. It is a one way reference of object where parent view has it's reference in a secondary(custom) view. However there is no reference of that secondary view in the parent view. So unless that situation arises, you are quite safe as far as retain cycle is concerned and there is no need of a weak reference of the parent view.

Get superView of custom class

I have a custom class myCustomClass which is a subclass of UITextField. (I'm going to call myCustomClass in a viewControllers class.)
In myCustomClass, I'm trying to check what kind of viewController the class that called it is. (UIViewController, UITableViewController etc.)
I tried:
if ([self.superview.nextResponder isKindOfClass[UIViewController class]]) {
NSLog(#"View Controller");
} else if ([self.superview.nextResponder isKindOfClass[UITableViewController class]) {
NSLog(#"TableView Controller");
}
I only get a result if the superclass is a viewController. So I did the following:
NSLog(#"%#", self.superview.nextResponder);
Results
UIViewController Class - ViewController
UITableViewController Class - UITableViewCell
How can I check if it's a UITableViewController?
For your specific case, you can use [self.superView isMemberOfClass:[UITableViewCell class]] to check if your custom view is inside a table view cell, which (unless you are using tableViewCell in an unusual way!) means that it's being called from a UITableViewController.
More generally, if you wanted to find out the containing view controller, you can recursively walk up the responder chain to find the containing viewController as in the second answer in this post:
Get to UIViewController from UIView?
It's also important to note that there is a difference between isKindOfClass: and isMemberOfClass:
isKindOfClass returns YES if 'the receiver is an instance of given class or an instance of any class that inherits from that class.'
isMemberOfClass returns YES if ' the receiver is an instance of a given class.'
Therefore, your UITableViewController, which inherits from UIViewController, will answer YES to isKindOfClass:[UIViewController class], will your if statement to act unexpectedly. (Though in the example it also didn't work correctly because you still needed to walk up the responder chain further).
So, if you in fact are comparing a UIViewController to a UITableViewController use -isMemberOfClass and your logic in the example would work as expected.

Why do we set delegate to nil at dealloc if the object is going to be destroyed anyway?

-(void) scrollViewDidScroll:(UIScrollView *)scrollView
{
PO(NSStringFromCGPoint(self.tableView.contentOffset));
PO(NSStringFromUIEdgeInsets(self.tableView.contentInset));
while(false);
}
-(void)dealloc
{
PO(NSStringFromClass([self class]));
PO(#"Deallocated");
self.tableView.delegate=nil;
}
Here I need to set self.tableView.delegate = nil to avoid error.
I am aware, from my previous question, that self.tableView.delegate won't automatically become nill when the delegate is destroyed. That's because the type of delegate is assign reference instead of weak reference.
However, what about self.tableView?
The only thing with strong reference to self.tableView is it's superview that's owned by self and self itsef.
So when self is destroyed, self.tableView should be destroyed too and that means self.tableView.delegate will be gone too.
So why do I need to set self.tableView.delegate=nil;
You need to set delegate to nil in many cases. In your case tableView can be referenced by some external class and will not destroyed after your class dealloc method. And will continue call its delegate method leading to crash.
There are several classes that works in another thread (NSURLConnection for example). Even if you release it it can continue calls delegate methods since it is retained in another thread.
If you hold the only reference to self.tableView, there's no need of setting the delegate to nil.
The only situation where you have to set the delegate to nil, if is another class has your class as a delegate, because if your class is destroyed, that other class will look for your class to implement some methods, and your call won't be there.
Lets say we have a SpriteKit scene where you have a gesture recognizer and you set its delegate.
Then you dealloc this scene and its recognizer from its controller.
It would lead to crash if the delegate method was called in scene during this process.
Actually this is not required. We are doing this to avoid forming retain cycle. We should not create a delegate with strong reference. If you accidentally created a delegate with strong reference then both parent and child will not get released. In that case dealloc itself will not get called. So it s not necessary.

ARC ownership with strong and weak references

In apples example
MyViewController *myController = [[MyViewController alloc] init…];
MyViewController * __weak weakMyController = myController;
myController.completionHandler = ^(NSInteger result) {
MyViewController *strongMyController = weakMyController;
if (strongMyController) {
[strongMyController dismissViewControllerAnimated:YES completion:nil];
}
else {
// Probably nothing...
}
};
What is happening here? I'm confused about:
MyViewController *strongMyController = weakMyController;
Does that mean weakMyController has a strong reference to it, so it would be like weakMyController's retain count + 1? What happens when you create a strong reference to a weak iVar?
Does that mean weakMyController has a strong reference to it, so it
would be like weakMyController's retain count + 1?
The retain count for myController is the same for all the variables that have its reference. It's a value of the object, not of the variables pointing to it. And it tells the runtime ho many strong references there exist pointing to the object.
So, the line
MyViewController *strongMyController = weakMyController;
will increment that count by 1, and ensure that as long as we have that variable in scope, that view controller won't be released.
In most cases it's enough to call methods on the weak reference inside the block (weakMyController in your example). I think that in this case they use a strong reference because there's an animation involved (so the view controller needs to exist for the duration of the animation, which would not be guaranteed if we used a weak reference).
To respond to the other part of your question, all the strong and weak references to an object hold the same value (the memory address of the object). The difference between strong and weak is what happens when they get their values. In the case of a weak reference, the retain count stays the same, while with a strong reference it gets incremented.

How to initialize a custom view(controller) so it works both programmatically and in Interface Builder?

Suppose you implement a custom table view and a custom view controller (which mostly mimics UITableViewControllers behaviour, but when initialized programmatically, ...
#interface Foo : MyCustomTableViewController ...
Foo *foo = [[Foo alloc] init];
... foo.view is kind of class MyCustomTableView instead of UITableView:
// MyCustomTableView.h
#protocol MyTableViewDelegate <NSObject, UITableViewDelegate>
// ...
#end
#protocol MyTableViewDataSource <NSObject, UITableViewDataSource>
// ...
#end
#interface MyCustomTableView : UITableView
// ...
#end
// MyCustomTableViewController.h
#interface MyCustomTableViewController : UIViewController
// ...
#end
How should you implement/override init methods in correct order/ways so that you could create and use an instance of MyCustomTableView both by subclassing MyCustomTableViewController programmatically or from any custom nib file by setting custom class type to MyCustomTableView in Interface Builder?
It important to note that this is exactly how UITableView (mostly UIKit for that matter) works right now: a developer could create and use either programmatically or by creating from nib, whether be it File owner's main view or some subview in a more complex hierarchy, just assign data source or delegate and you're good to go...
So far I managed to get this working if you subclass MyCustomTableViewController, where I will create an instance of MyCustomTableView and assign it to self.view in loadView method; but couldn't figure out how initWithNibName:bundle:, initWithCoder:, awakeFromNib, awakeAfterUsingCoder:, or whatever else operates. I am lost in life cycle chain and end up with a black view/screen each time.
Thanks.
It is a real mystery how the UITableViewController loads its table regardless of if one is hooked up in interface builder, however I have came up with a pretty good way to simulate that behavior.
I wanted to achieve this with a reusable view controller that contains a MKMapView, and I figured out a trick to make it happen by checking the background color of the view.
The reason this was hard is because any call to self.view caused the storyboard one to load or load a default UIView if didnt exist. There was no way to figure out if inbetween those 2 steps if the user really didn't set a view. So the trick is the one that comes from a storyboard has a color, the default one is nil color.
So now I have a mapViewController that can be used in code or in storyboard and doesn't even care if a map was set or not. Pretty cool.
- (void)viewDidLoad
{
[super viewDidLoad];
//magic to work without a view set in the storboard or in code.
//check if a view has been set in the storyboard, like what UITableViewController does.
//check if don't have a map view
if(![self.view isKindOfClass:[MKMapView class]]){
//check if the default view was loaded. Default view always has no background color.
if([self.view isKindOfClass:[UIView class]] && !self.view.backgroundColor){
//switch it for a map view
self.view = [[MKMapView alloc] initWithFrame:CGRectZero];
self.mapView.delegate = self;
}else{
[NSException raise:#"MapViewController didn't find a map view" format:#"Found a %#", self.view.class];
}
}
The strategy I've used when writing such classes has been to postpone my custom initialization code as late as possible. If I can wait for viewDidLoad or viewWillAppear to do any setup, and not write any custom code in init, initWithNibName:bundle: or similar methods I'll know that my object is initialized just like the parent class no mater what way it was instantiated. Frequently I manage to write my classes without any overrides of these init methods.
If I find that I need to put my initialization code in the init methods my strategy is to write just one version of my initialization code, put that in a separate method, and then override all the init methods. The overridden methods call the superclass version of themselves, check for success, then call my internal initialization method.
If these strategies fail, such that it really makes a difference what way an object of this class is instantiated, I'll write custom methods for each of the various init methods.
This is how I solved my own issue:
- (void)loadView
{
if (self.nibName) {
// although docs states "Your custom implementation of this method should not call super.", I am doing it instead of loading from nib manually, because I am too lazy ;-)
[super loadView];
}
else {
self.view = // ... whatever UIView you'd like to create
}
}

Resources