Why system call UIApplicationDelegate's dealloc method? - ios

I have next code:
// create new delegate
MyCustomApplicationDelegate *redelegate = [[MyCustomApplicationDelegate alloc] init];
redelegate.delegate = [(NSObject<UIApplicationDelegate42> *)[UIApplication sharedApplication].delegate retain];
// replace delegate
[UIApplication sharedApplication].delegate = redelegate;
...
[UIApplication sharedApplication].delegate = redelegate.delegate;
[redelegate.delegate release];
after last line the system called dealloc method of base UIApplicationDelegate class.
So, why? I read Apple documentation about UIApplication, about delegate property:
#property(nonatomic, assign) id delegate
Discussion The delegate must adopt the UIApplicationDelegate formal
protocol. UIApplication assigns and does not retain the delegate.
It clearly says that UIApplication assigns and does not retain the delegate. So, why it destroy my base delegate?

UIApplication has some unusual behavior. The first time that you set the delegate property on UIApplication, the old delegate is released. Every time after that, when you set the delegate property the old delegate is not released.
The declaration for the delegate property in UIApplication.h is:
#property(nonatomic,assign) id<UIApplicationDelegate> delegate;
This implies that the shared UIApplication will never call retain or release on the delegate. This is normal for the delegate pattern: A class normally doesn't retain its delegate because this results in a retain loop.
But in this case, there's an unusual problem: Who owns the app's first delegate? In main.m the call to UIAplicationMain() implicitly allocs and inits the first delegate, which leaves that delegate with a retain count of 1. Someone has to release that but there's no classes around to own it. To solve this, whenever you set a new delegate on UIApplication for the first time, it releases that first delegate. The new delegate was alloced and initted by some class in your app, so you already own a reference to the new delegate. UIApplication doesn't retain or release the new delegate.

I don't think you're supposed to change the UIApplication sharedApplication delegate like this. The standard delegate of your App is automatically the [UIApplication sharedApplication] delegate. So perhaps you should just put all your custom code in the normal delegate?

I have an idea that old delegate after this:
[UIApplication sharedApplication].delegate = redelegate.delegate;
will be released.
But when i see this
#property(nonatomic, assign) id<UIApplicationDelegate> delegate
i'm thinking that it should not (because assign)
Do you agree with me?

Related

iOS - Monitoring app flow from AppDelegate

I am trying to create Singleton that it would be initialised from AppDelegate. The purpose is to monitor all the UIViewControllers (the active one) and print on console the kind of class (as proof of concept). So my basic idea is to initialise the singleton in AppDelegate and pass as parameter a reference of AppDelegate. Then somehow I must monitor which one is the active view.
For example: View A B C
A is the first view in Navigation Controller. My Singleton knows that the current view is A. Then we push view B and Singleton is notified that view B is now the current view. Same with C. Now we pop C and Singleton knows that the current view is B.
Is any kind KVO or NSNotification for notifying my singleton that a new UIView is appeard/removed? Any alternatives for this problem?
After registering for all notification I found out about UINavigationControllerDidShowViewControllerNotification.
With this observer:
[notifyCenter addObserver:self selector:#selector(viewAppeared:) name:#"UINavigationControllerDidShowViewControllerNotification" object:nil]; I am able to monitor the activity of the UINavigationController.
You can get current View controller by just making a view controller object in appdelegate like
#property (strong, nonatomic) UIViewController *currentViewController;
and then on the view will Appear of your current view controller give the current reference to the app delegate object like
AppDelegate *myAppd = (AppDelegate*)[[UIApplication sharedApplication]delegate];
myAppd.currentViewController = self;
This way you get your current active view.
One approach is to pick a particular method you want to know about and intercept it.
Here, I create a category on UIViewController and provide a method I want called whenever the controller's viewWillAppear: would normally be called:
#include <objc/runtime.h>
#implementation UIViewController (Swap)
+ (void)load
{
NSLog(#"Exchange implementations");
method_exchangeImplementations(
class_getInstanceMethod(self, #selector(viewWillAppear:)),
class_getInstanceMethod(self, #selector(customViewWillAppear:)));
}
- (void)customViewWillAppear:(BOOL)animated
{
// Call the original method, using its new name
[self customViewWillAppear:animated];
NSLog(#"Firing %# for %#", VIEW_CONTROLLER_APPEARED, self);
[[NSNotificationCenter defaultCenter] postNotificationName:VIEW_CONTROLLER_APPEARED
object:self];
}
#end
After that, it's just a case of listening for the notification in whatever object needs to know (e.g. your Singleton).

Delegate that's alive until the final method is invoked, but under ARC

In my non-ARC iOS code, I use the following pattern: a delegate proxy class that forwards a single delegate message to some other class, then releases self. Here's an example for UIAlertView:
#interface AlertCallback : NSObject
<UIAlertViewDelegate>
{
NSObject *m_Sink;
SEL m_SinkSel;
}
-(id)initWithSink:(id)Sink SinkSel:(SEL)Sel;
#end
#implementation AlertCallback
-(id)initWithSink:(id)Sink SinkSel:(SEL)Sel
{
if(self = [super init])
{
m_Sink = Sink;
m_SinkSel = Sel;
}
return self;
}
- (void)alertView:(UIAlertView *)av didDismissWithButtonIndex:(NSInteger)n
{
//Call the callback
[m_Sink performSelector:m_SinkSel withObject:#(n)];
[self autorelease];
}
#end
//And finally usage:
AlertCallback *del =
[[AlertCallback alloc]
initWithSink:self
SinkSel:#selector(OnIAmSure:)];
UIAlertView *av = [[UIAlertView alloc] initWithTitle:nil
message:#"Are you sure?"
delegate: del
cancelButtonTitle:#"No"
otherButtonTitles: #"Yes", nil];
The idea here is that the proxy object will stay alive until the user taps a button, at which point the proxy will invoke its host's method and commit suicide. I'm using a similar pattern for action sheet and connections.
This doesn't translate to ARC. The delegate on the UIAlertView is weak. With only a weak ref to it, the AlertCallback with be released right away.
I can see several ways to overcome this. The callback can hold a reference to self (a deliberate ref loop) and nil it when the delegate message comes. It's also possible to derive a class from UIAlertView, implement the delegate protocol, and make it designate self as the delegate - but overriding the init method would be tricky; I don't know how to override a variadic method, passing an unknown number of parameters to the superclass. Finally, I could build a category on top of UIAlertView, specifying self as delegate, and use objc_setAssociatedObject for extra data items. Clunky, but it might work.
Is there a preferred/recommended way to implement this pattern under ARC?
Your first solution, keeping a self reference, works fine - see for example Manual object lifetime with ARC.
If you cannot, or do not wish to, modify the class to manage its own lifetime then a standard solution is to use associated objects. This is a standard runtime feature which effectively allows the lifetime of one object to be linked to that of another. In your case you can associate your delegate to your UIAlertView, effectively making the delegate reference strong rather than weak. Many questions on SO deal with associated objects, for example see Is it ever OK to have a 'strong' reference to a delegate?.
HTH

When to unregister a delegate of a UIView or UILabel

I got a customized UILabel which register itself to a communication instance during creation. This part works perfect, the UILabel will . But when I remove the UILabel will update itself after called by the communication instance. I have of course remove the listener delegate too. But as ViewDidUnload is not call anymore, I do not know where.
Here the code sample:
#implementation MyValueLabel
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self)
{
self.text=[NSString stringWithFormat:#"%.02f", ((double)g_com.getRemoteValue())/100 ];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate addBalanceListener:self];
}
return self;
}
-(void)communicationUpdate
{
self.text=[NSString stringWithFormat:#"%.02f", ((double)g_com.getRemoteValue())/100 ];
}
// This is the missing Method
-(void) DestructionOfTheLabelWhichIDidNotFound
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate removeBalanceListener:self];
}
#end
Technically you seem to be looking for the -dealloc method which is called on an object when it is to be deallocated. However I do not believe that is an adequate solution.
The example provided here seems to be doing a number of unexpected things which I believe will result in code which is surprising to other developers, requires fighting against common patterns in the UIKit framework, and be difficult to maintain.
Normally iOS view controllers are responsible for mediating or coordinating how data reaches view objects. A controller supplies a view with either the data it should currently display or provides it with a source of that data. This allows a single view class to be used in many different locations and to display data of the same type regardless of its source. Instead here we have a view which reaches out to obtain it's own data. There's no way for a controller to determine when an instance of this view class should update or what data it should display. For example the controller cannot decide to pause updates, or cause other view elements to update at the same time.
In addition this view makes several assumptions about the source of it's data and how that source may be obtained. The view assumes that the current application's app delegate is specifically an AppDelegate class so it will not work if you want to use this view in another application. Since the view also obtains this AppDelegate instance via a call to + sharedApplication there's no hint to users of this view that it has this dependency.
Consider instead allowing classes which use this view class to provide it with the data it should display.
If the property where addBalanceListener: stores the reference is weak, you don't need to do anything. Weak properties are automatically set to nil when the instance they are pointing to is dealloced.

Delegate is nil

I have a property on a ViewController which I set from a parent SplitViewController:
Property declaration/synthesization
#interface DownloadRecipesViewController_iPad : DownloadRecipesViewController<PopoverMenuDelegate, RecipeChangeDelegate>{
id <NSObject, PopoverMenuParentDelegate, RecipeChangeDelegate, RecipeDownloadedDelegate> _selectionDelegate;
}
#property (strong) UIBarButtonItem *btnMenu;
#property (strong) id <NSObject, RecipeChangeDelegate, RecipeDownloadedDelegate> selectionDelegate;
#implementation DownloadRecipesViewController_iPad
#synthesize btnMenu;
#synthesize selectionDelegate = _selectionDelegate;
I wire up the delegate in the parent SplitViewVC's viewDidLoad method:
Wiring up the delegate
self.downloadVc = [self.storyboard instantiateViewControllerWithIdentifier:#"RecipeDownload"];
[self.downloadVc setSelectionDelegate:self];
A button in the Child VC calls a method to fire an event up to the parent ViewController, but when this event is called, the delegate is nil and the event isn't fired. I've wracked my brains trying every which way to find out why this happens but I'm at a total loss.
Delegate is nil here (firing the delegate):
-(IBAction)didTapMenu:(id)sender{
if([_selectionDelegate respondsToSelector:#selector(shouldToggleMenu)])
[_selectionDelegate shouldToggleMenu];
}
I've also tried without the backing property but hit the same problem.
Suggestions on how to find this follow. But first, why not save yourself some typing and remove the ivar and remove the #synthesize - its totally unnecessary typing at this time. Also, as a comment said, delegates should almost always be typed as weak.
Suggestions:
1) Write a (temporary) setter for the selectionDelegate, then set a break point where you actually set the value (or after) so you can verify that its getting set, and that nothing else is zeroing it out.
2) Set a breakpoint on the IBAction method, on the line where the if statement is, and when you hit it verify the object is the same one where you set the delegate, what the delegate value is, and then see if the respondsTo method succeeds (use single step).
The way I eventually solved this was:
Create a new delegate which exposes a method: -(void)segueBeginning:(UIStoryboardSegue*)destination
Use this delegate to expose prepareForSegue from the child UINavigationController to the parent SplitViewController
.3. Hook up my child VC in the parent ViewController when prepareForSegue is raised in the child nav controller:
if([destination.destinationViewController isKindOfClass:[DownloadRecipesViewController_iPad class]] && !self.downloadVc){ // download recipes
self.downloadVc = destination.destinationViewController;
self.downloadVc.selectionDelegate = self;
[self.downloadVc setSnapshotChangeDelegate:self];
[self.downloadVc.navigationItem setRightBarButtonItems:[NSArray arrayWithObjects:btnAdd, nil]];
}

Why do AppDelegates have an instance variable for the root controller?

This example is taken from The Big Nerd Range iPhone book (page 143/144) - ItemsViewController is a subclass of UITableViewController:
#interface HomepwnerAppDelegate : NSObject <UIApplicationDelegate>
{
UIWindow *window;
ITemsViewController* itemsViewController;
}
....
itemsViewController = [[ItemsViewController alloc] init];
[window setRootViewController: itemsViewController]
My question is why is it necessary to have the iVar itemsViewController, why not just do this instead:
...
window.rootViewController = [[ItemsViewController alloc] init];
I presume the window will destroy its rootViewController when the app exits and thus there's no leaks, and the window will be in existence for the lifetime of the app so I don't understand why this and many other example have a separate iVar for the root controller?
TIA
The biggest advantage is simply that you can access the methods of your view controller without needing to cast over and over again:
[itemsViewController doSomething];
// vs.
[(ItemsViewController *)window.rootViewController doSomething];
Depending on the app you might need to refer to the root view controller frequently from the app delegate, for example when implementing the handlers for entering background/foreground and similar app delegate callbacks.
There is absolutely no need to keep the ivar around if you don't need it.
BTW, you will leak ItemsViewController if you don't autorelease it. (Unless you are using ARC)
The reason is historical, I think. Back when that book was written, the window and root view controller both used to be IBOutlets and were set from a nib file called MainWindow.nib.
Also, UIWindow didn't used to have a rootViewController property to assign the control to (the root view controller.view was just added directly as a subview to window), so if you didn't store it in an ivar then it wouldn't be retained by anything and your app wouldn't work because the root view controller would be released as soon as it was created.
These days however, since iOS4 and now ARC, the base project template has been updated and doesn't even have ivars any more (which are no longer needed). It does still have an #property for the view controller, but it's technically not needed any more either, and your alternative solution of assigning a new controller directly to the window.rootViewCOntroller would work fine.
This is totally stylistic choice. There are other ways to get the convenience accessor. I don't ever create an ivar in my rootViewController never changes. I will usually go for a read only property.
#property (nonatomic, readonly) MyRootViewController *rootViewController;
- (MyRootViewController *)rootViewController {
if ([self.window.rootViewController isKindOfClass:[MyRootViewController class]) {
return (MyRootViewController *)self.window.rootViewController;
}
return nil;
}

Resources