calling super class question - objective-c

I'm wondering what happens in this case of class hierarchy
MySuperClass : UIViewController
MYSubClass : MySuperClass
MySuperClass lack the method, ViewWillAppear
My question is: if MySubClass has the following method
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
is that code simply ignored (won't be executed) or viewWillAppear in UIViewController will be called?
Just wondering.

It works similarly to normal method calls. When you call on super, the runtime goes up through the chain of superclasses until it finds one that implements the requested method. If it doesn't find one, it will call forwarding methods, and if the method isn't forwarded it will call doesNotRecognizeSelector:. So, yes, viewWillAppear will be called on the UIViewController class.

Say you have:
MySubClass *mySubController = [[MySubClass alloc] initWithNibNamed:nil bundle:nil];
If you do something with mySubController that will cause its view to appear, like push it onto a nav controller's stack, then MySubClass' implementation of -viewWillAppear will be called. As it is now, that implementation just calls super's implementation. Since MySuperClass doesn't override -viewWillAppear, UIViewController's implementation will be called.

Related

Remove self as delegate from all

There's a way to remove self as observer from all notifications:
[[NSNotificationCenter defaultCenter] removeObserver:self];
Is there a similar way to remove self as a delegate of all objects that the viewController is a delegate of?
I want to place it in dealloc method in my prototype viewController. The reason is that sometimes when I dismiss a viewController, I get a crash with a message that a UIScrollView (or any of the official subclasses) called a method on its delegate (the viewController) that has since been deallocated. Since I consciously intend to call no such methods, I'd rather set self.delegate = nil. But since this is a prototype viewController, I don't have outlets to those scrollViews, hence my question.
No, there is no such way. Once we are done with the delegate, it requires to simply nil that reference.
Also before calling any delegate method make sure, to check nil condition as well method availability condition like:
if (_delegate != nil && [self.delegate respondsToSelector:#selector(myDelegateMethod:)]) {
// then only call that delegate method
[self.delegate myDelegateMethod:param];
}
Once you are done with all your delegate things nil your reference:
self.delegate = nil;
I don't know any built-in mechanism for it. I think, that the code that was responsible for making your object a delegate should be responsible for freeing it of this responsibility. You could provide some interface for it, but it depends on your code.
Also, since delegates are weak-referenced, they will be automatically set to nil when the delegate object is deleted, but it's not your case, I believe.
UPDATE:
Since in your case delegates don't seem to be declared as weak, I guess, the only option is to keep track of all objects that set your viewController as their delegate manually.
For instance:
In your ViewController:
#interface YourViewController
#property (nonatomic, strong) NSMutableDictionary *objectsThatDelegateSomethingToUs;
#end
#implementation YourViewController
-(void)makeDelegateOfObject:(id)obj withDelegatePropertyName:(NSString*)delegatePropertyName {
[self.objectsThatDelegateSomethingToUs setObject:delegatePropertyName forKey:obj];
}
-(void)dealloc {
for (id obj in self.objectsThatDelegateSomethingToUs.allKeys) {
[obj setValue:nil forKey:[self.objectsThatDelegateSomethingToUs valueForKey:obj]];
}
}
#end
Where you set your viewController as a delegate:
scrollView.delegate = viewController;
[viewController makeDelegateOfObject:scrollView withDelegatePropertyName:#"delegate"];
But, sadly, in this case you'll have to set your ViewController as a delegate programmatically. If delegate property is always called delegate, NSMutableArray should do the trick.
Actually, it's weird that scrollView keeps working when it's parent ViewController is deallocated. May be this is the real problem, and it can be fixed somehow, but, unfortunately, I can't give you any advice on it now, so my answer is trying to deal with the problem that you originally asked about. I recommend you to leave the question open for a while, may be someone else will be able to suggest a better solution.
P.S. Check out Logan's comment to your question. If all objects that use your ViewController as their delegate, are parts of ViewControllers' view hierarchy, then his solution is simpler and more elegant.
Placing this code in my superclass' dealloc method solved my crashes:
for (id view in self.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
[view setDelegate:nil];
}
}
I was considering doing this in my viewController where I have direct access to the UIScrollView. However, because ARC disallows calling [super dealloc], this does not allow the dealloc code in my superclass to be called.

Trying to set a property value, but it's nil

I declared a protocol in the header file of a Controller that manages a map view.
#protocol UCMapViewDelegate <NSObject>
#required
- (void)pushMapviewRight;
#end
I'm declaring the implementation of the protocol in another view controller (.h) and implement it in the .m file
// in the UCRootViewController.h
#interface UCRootViewController : UIViewController <UCMapviewDelegate>
// in the UCRootViewController.m
- (void)pushMapviewRight
{
NSLog(#"push mapview right");
}
I'm setting the delegate to a property that points to the rootviewController. This is done in the viewDidLoad() of my MapviewController, with a property #property (weak, nonatomic) id<UCMapViewDelegate> delegate;.
// in UCRootViewController
self.mapviewController.rootviewController = self;
// in UCMapViewController
self.delegate = (id<UCMapviewDelegate>)self.rootviewController;
Calling the delegated method. showMenu() gets executed when a button in the mapviewController gets pressed and it works. but the delegate method does NOT get called.
- (void)showMenu
{
NSLog(#"show menu");
[self.delegate pushMapviewRight];
}
But nothing happens.. what is wrong?! Help is greatly appreciated!
I fixed it. At first I used NSLog to verify that self was not nil (which is pretty obvious
, but still) I'm actually not sure why, but self.mapviewController.rootviewController = self; did not "carry over" to the point where I wanted to reference self.rootViewController, although self was not nil at the point where I set it to be the pointer to rootViewController.
I fixed it by creating another initWithRootViewController:(UCRootViewController*) ctrland passed self as an argument when I created the MapViewController.
Can someone explain why the valid reference to self (=rootViewController), was not available in the MapViewController?
Is the rootViewController property a strong reference or a weak one? It should probably be strong. If there are no weak references to the object, then it will be immediately released, and weak references get nilled out when the objects they point to are released. You need a strong reference somewhere in your application for objects to hang around.
As #CodaFi said, your codes look messy. Why do you set delegate property value in UCMapViewController? The delegate should be set in its parent when popup or prepare for the segue.
Basically, if your UCMapViewController has knowledge about UCRootViewController implementing a delegate method, why not call its method from riitVuewController directly? No need to set delegate at all.
Here is one example of using storyboard and segue, UCMyViewController is going to push segue to UCMapViewController:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
...
[segue.destinationViewController setDelegate:self.rootViewController];
...
}

What is [super awakeFromNib]; used for?

I often see this line of code
[super awakeFromNib]
in the awakeFromNib method in the implementation of a view controller
My understanding is it is telling the super class of this view controller (which would be the window) to awakeFromNib.
Am I right? If so, why do we have to tell the window to awake in the awakeFromNib method of a UIView Controller sub-class?
My understanding is it is telling the super class of this view controller ...
right so far...
(which would be the window)
oops - that's the source of your confusion.
The "super class of the view controller" is UIViewController. "super" is referring to the base class that your UIViewController sub-class inherits from; it doesn't have anything to do with the window that encloses your view.
So, what this is doing is invoking the default awakeFromNib implementation of a basic UIViewController, in addition to whatever you're doing in your sub-class implementation.
What David said above is correct,
Now for your question "why do we have to tell the window to awake in the awakeFromNib method of a UIView Controller sub-class?"
if there is any custom modification or any data that you want to load before your ViewController loads we should use the awakeFromNib.
awakeFromNib is called when the controller itself is unarchived from a nib.

objective-c: Calling a void function from another controller

i have a void, like -(void) doSomething in a specific controller.
i can call it in this controller via [self doSomething], but i don't know how to call this void from another .m file.
I want to call it in a -(IBAction) action:(id)sender
I've tried using performSelector, but i got a 'unreconiezd selector send' in the log.
I've no idea if i have to use the notification center, or delegate...
Thanks,
ronan.
You have to have an instance of the first controller (the one where you declare the function in) in your second controller.
FirstViewController *firstController = [[FirstViewController alloc] init];
[firstController doSomething];
If your first controller is declared somewhere else, and you want your second controller to know about it, have a property of FirstViewController type in your second controller, and initialize it when you need it with your FirstViewController instance.
Sometimes, this can be quite complicated to do if you have multiple controller instances and you want every one of them to know about all the others, so I would rather suggest rewriting your method to a class method:
+ (void) doSomething;
so you could call it from anywhere with
[FirstViewController doSomething];
if you have the object you want to call it on and the method is public, then you just:
[object doSomething];
The problem is that you have two controllers who don't know about each other's existence. The notification center certainly is the way to deal with this situation: the advantage is that you need not establish a formal connection between the two.
The delegate method is another possibility, but then you have to either establish the connection via an IBOutlet in Interface Builder, or you must have one controller create the other controller and pass itself as the delegate. This ties them together more closely, which may or may not be appropriate. If the only connection between the two controllers is the calling of one -(void) method, then I'd go with NSNotification.

NSApplicaton delegate - applicationDidFinishLaunching

I have declared a delegate for my cocoa application here :
MyAppDelegate.h
#interface MyAppDelegate : NSApplication {
}
- (void) applicationDidFinishLaunching:(NSNotification*) notice ;
#end
MyAppDelegate.m
#implementation MyAppDelegate
- (void) applicationDidFinishLaunching:(NSNotification*) notice {
NSLog(#"inside appdidfinishlaunching") ;
}
#end
I have linked the delegate outlet of File Owner to this object in IB.
Yet, this method is not getting called. I don't see any log messages from it.
Can you please suggest what is wrong ?
Your application delegate is not an application itself. It should inherit from NSObject, not NSApplication.
Why that matters
NSApplication is a singleton. Its init method always returns the first instanceĀ of NSApplication or any subclass, throwing away any subsequent objects you (or the nib loader) may be calling init on.
So you ended up setting your application object as its own delegate. The object you intended to make the delegate died in the second call to init, and the application object took its place.
Changing the application object to be an instance of your subclass would also have worked, but you'd still have the application as its own delegate, which is unclean and possibly dangerous (NSApplication may privately implement some of its delegate methods itself, as they're just notification handler methods). The only correct solution is to make your app delegate class not inherit from NSApplication.