On iOS and Cocoa Touch, should we assume we should always send "super" the same message? - objective-c

In iOS and Cocoa Touch, sometimes it seems we can get by without calling super, such as:
-(void) viewDidAppear:(BOOL)animated {
// nothing weird if the following is not called:
// [super viewDidAppear: animated]
// but the docs says we should call it
}
and I think with drawRect, we usually won't send super that same message, unless the super is doing some drawing of its own. Is it true that we should always send super the same message just to be safe in Cocoa Touch and iOS, and are there cases, then, not not send super the same message?

There's no general rule, it always depends on the class you're subclassing and method you're overriding. NSObject is the root of the class hierarchy in (modern) Objective-C and obviously it doesn't implement most of the methods you see and use, so always calling super wouldn't make sense as a general rule.
You just have to check the documentation for each method separately.

You can't say this in general. Only one thing is sure: call super if you want the functionality that the method being overridden does, and don't call it if you don't.

Related

Removing an action from a subclass

My NSDocument subclass implements selectAll:. Only problem is, I'm using NSTableView, and it also implements selectAll:. However, the selectAll: action in NSTableView doesn't do what I want, and it does prevent the selectAll: method in my Document class from ever being reached in the responder chain.
I already have a subclass of NSTableView, and after poking around a bit I got things working the way I want by adding a respondsToSelector: method to my NSTableView subclass which lies to the runtime by telling it there is no selectAll: action:
-(BOOL)respondsToSelector:(SEL)targetSelector
{
if (targetSelector == #selector(selectAll:)) {
return FALSE; // we don't want tableView's implementation of selectAll
}
return [super respondsToSelector:targetSelector];
}
This seems to work fine, allowing the selectAll: method in my document subclass to do its thing. But this solution leaves me a bit uneasy. What about other action methods I have implemented in this subclass? Do I need to manually check and return true for each of them? I do have two actions defined in this subclass, moveLeft: and moveRight:, and they seem to work, even though I am not handling them in respondsToSelector:. So my question is, am I doing this correctly, or is there something I am missing? Or perhaps there is some entirely different way to do this properly?
By the way, I got the idea of overriding respondsToSelector from this post on the OmniGroup forum:
http://mac-os-x.10953.n7.nabble.com/Removing-an-action-from-a-subclass-td27045.html
Sending a message to super affects which implementation of that method we use. It doesn't change who self is.
So let's try to imagine how respondsToSelector: works. Given a selector mySelector, it probably introspects every class up the superclass chain, starting with [self class], to see whether it actually implements mySelector.
Now then, let's say your subclass is called MyTableView. When MyTableView says
[super respondsToSelector:targetSelector]
what happens? The runtime will look up the superclass chain for another implementation of respondsToSelector:, and eventually will find NSObject's original implementation. What does that implementation do? Well, we just answered that: it starts the search for an implementation of targetSelector in [self class]. That's still the MyTableView class! So if you have defined moveLeft: in MyTableView, respondsToSelector: will find it and will return YES for moveLeft:, exactly as you hope and expect.
Thus, to generalize, the only selector for which this search has been perverted is the search for selectAll: - exactly as you hope and expect. So I think you can relax and believe that what you're doing is not only acceptable and workable but the normal solution to the problem you originally posed.
You might also like to look at the Message Forwarding chapter of Apple's Objective-C Runtime Programming Guide.

multiple delegates in objective-c

i have been working on moving one of my apps away from the "shared appdelegate" process which seems to be frowned up, despite its over whelming use. i have been attempting to setup protocol methods for what i want to do but am having zero luck. my question is, can you even have lets say a single viewcontroller send delegate requests to multiple classes? from what im finding out it doesn't seem like you can. which doesn't make sense because i thought that was the whole point of delegates and protocols with mvc. now just to clarify, i know you can have a single viewcontroller act as the delegate for multiple other viewcontrollers. but that's not what i am asking. for a simple example, lets say you have apples flip-utility template. the "done" button just calls a delegate method to the mainvc to dismiss it. now lets say we added a new class called...
#interface NewClass : NSObject <TheOtherDelegate>
and it had a delegate method...
- (void)doSomething
{
NSLog(#"The Delegate did something...");
}
can we have a button on the flipsideviewcontroller, that we wanted to call that delegate method, but still keep the "done" button call to the delegate method on the mainviewcontroller that dismisses it?
that being said, i put together a quicky project just to see if it would work and it doesn't. i came across an "answer" that says you have to instantiate the class first you want to be the delegate...
NewClass *myDelegate = [NewClass alloc] init]
[fillInMethodHere setDelegate:myDelegate];
not sure why it got a correct answer check, because needless to say it doesn't work. is there something i am missing? i scoured ib to see if there is some "delegate" connection somewhere but i couldn't find anything.
on a side note, as i was working in my working project, i read a suggestion about removing the #import and adding #class. again, that broke all kinds of things. the strange thing is before doing that, what i had so far was working and building fine. when i removed the new #class and un-commented the #import. xcode all of a sudden gave me an error "cannot find protocol deceleration for..." but yet, it worked seconds earlier. i would up having to remove the protocol code and re-add it for it to work again. very starge.
any help would be appreciated. everything iv read in docs, google, stack, etc that say something should work, don't in an actual project.
A "delegate" isn't some fancy object. It's simply a synthesized property of type id called delegate. If you wanted to, you could have an arbitrary number of properties that all conformed to the same protocol. Then when you wanted to issue a callback, you would just address all of them:
[self.mydelegateA doSomething];
[self.mydelegateB doSomething];
etc.
You could also have an NSMutableArray property that you could add objects to, and then use [self.myMutableArrayOfDelegates makeObjectsPerformSelector:#selector(doSomething)].
Finally, there's always the route of NSNotificationCenter (not to be confused with push notifications) is a class that provides an inter-object messaging system. Many objects can register for a message that any other object can send.
Please see the Apple's documentation for more information. Click Here.
Regardless of the fact that this is OS X documentation, it's still quite good at explaining things visually: click here.
Here's an example of simply changing the name of the delegate property: click here
And here's an example of adding another protocol and a second delegate: click here
Finally, here's an example that builds on the previous two and has a third delegate that also conforms to the same protocol: click here

What invokes viewDidLoad when subclassing UIViewController?

Trying to get my head around protocols and delegates when extending it further into the UIKit framework's implementation.
From my understanding of this stackoverflow post a delegate method will usually have Did, Should & Will in it's name.
Based on this, I would assume that - (void)viewDidLoad; declared in UIViewController.h is a delegate method, but of what and from where?
I've looked at UIViewController's header file and it only adhere's to the NSCoding protocol which is a dead end. UIViewController's superclass UIResponder is also a dead end as far as I can see.
I've used viewDidLoad as an example here but this could apply to any of the Did, Should & Will methods in UIViewController.
Is this simply one of those cases that is an exception to the guidelines or am I missing something?
"did", "should", and "will" are words usually used to describe when a method is called, whether it is asking if it "should" do something", giving you a hook to run code before something "will" happen, or as a callback when something "did" happen. these words are usually used in delegate and callback methods.
viewDidLoad is called when your .nib file has been loaded into memory, and your IBOutlets have been instantiated and hooked up, and are ready for configuration. you don't need to worry about calling it yourself if you intend to subclass UIViewController, if that's what you're wondering.

Intercept method call in Objective-C

Can I intercept a method call in Objective-C? How?
Edit:
Mark Powell's answer gave me a partial solution, the -forwardInvocation method.
But the documentation states that -forwardInvocation is only called when an object is sent a message for which it has no corresponding method. I'd like a method to be called under all circumstances, even if the receiver does have that selector.
You do it by swizzling the method call. Assuming you want to grab all releases to NSTableView:
static IMP gOriginalRelease = nil;
static void newNSTableViewRelease(id self, SEL releaseSelector, ...) {
NSLog(#"Release called on an NSTableView");
gOriginalRelease(self, releaseSelector);
}
//Then somewhere do this:
gOriginalRelease = class_replaceMethod([NSTableView class], #selector(release), newNSTableViewRelease, "v#:");
You can get more details in the Objective C runtime documentation.
Intercepting method calls in Objective-C (asuming it is an Objective-C, not a C call) is done with a technique called method swizzling.
You can find an introduction on how to implement that here. For an example how method swizzling is implemented in a real project check out OCMock (an Isolation Framework for Objective-C).
Sending a message in Objective-C is translated into a call of the function objc_msgSend(receiver, selector, arguments) or one of its variants objc_msgSendSuper, objc_msgSend_stret, objc_msgSendSuper_stret.
If it was possible to change the implementation of these functions, we could intercept any message. Unfortunately, objc_msgSend is part of the Objective-C runtime and cannot be overridden.
By googling I found a paper on Google Books: A Reflective Architecture for Process Control Applications by Charlotte Pii Lunau. The paper introduces a hack by redirecting an object's isa class pointer to an instance of a custom MetaObject class. Messages that were intended for the modified object are thus sent to the MetaObject instance. Since the MetaObject class has no methods of its own, it can then respond to the forward invocation by forwarding the message to the modified object.
The paper does not include the interesting bits of the source code and I have no idea if such an approach would have side effects in Cocoa. But it might be interesting to try.
If you want to log message sends from your application code, the -forwardingTargetForSelector: tip is part of the solution.
Wrap your object:
#interface Interceptor : NSObject
#property (nonatomic, retain) id interceptedTarget;
#end
#implementation Interceptor
#synthesize interceptedTarget=_interceptedTarget;
- (void)dealloc {
[_interceptedTarget release];
[super dealloc];
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(#"Intercepting %#", NSStringFromSelector(aSelector));
return self.interceptedTarget;
}
#end
Now do something like this:
Interceptor *i = [[[Interceptor alloc] init] autorelease];
NSFetchedResultsController *controller = [self setupFetchedResultsController];
i.interceptedTarget = controller;
controller = (NSFetchedResultsController *)i;
and you will have a log of message sends. Note, sends sent from within the intercepted object won't be intercepted, as they will be sent using the original object 'self' pointer.
If you only want to log messages called from the outside (usually called from delegates; to see which kind of messages, when, etc.), you can override respondsToSelector like this:
- (BOOL)respondsToSelector:(SEL)aSelector {
NSLog(#"respondsToSelector called for '%#'", NSStringFromSelector(aSelector));
// look up, if a method is implemented
if([[self class] instancesRespondToSelector:aSelector]) return YES;
return NO;
}
Create a subclass of NSProxy and implement -forwardInvocation: and -methodSignatureForSelector: (or -forwardingTargetForSelector:, if you're simply directing it on to a second object instead of fiddling with the method yourself).
NSProxy is a class designed for implementing -forwardInvocation: on. It has a few methods, but mostly you don't want them to be caught. For example, catching the reference counting methods would prevent the proxy from being deallocated except under garbage collection. But if there are specific methods on NSProxy that you absolutely need to forward, you can override that method specifically and call -forwardInvocation: manually. Do note that simply because a method is listed under the NSProxy documentation does not mean that NSProxy implements it, merely that it is expected that all proxied objects have it.
If this won't work for you, provide additional details about your situation.
Perhaps you want NSObject's -forwardInvocation method. This allows you to capture a message, retarget it and then resend it.
You can swizzle the method call with one of your own, which does whatever you want to do on "interception" and calls through to the original implementation. Swizzling is done with class_replaceMethod().
A method call, no. A message send, yes, but you're going to have to be a lot more descriptive if you want a good answer as to how.
To do something when a method is called, you could try an events based approach. So when the method is called, it broadcasts an event, which is picked up by any listeners. I'm not great with objective C, but I just figured out something similar using NSNotificationCenter in Cocoa.
But if by "intercept" you mean "stop", then maybe you need more logic to decide wether the method should be called at all.

Checking for a valid delegate object before sending it a message

I am trying to implement the delegate Pattern in Objective-C, however I am experiencing a Bad Access exception when invoking the delegate sometimes. It seems this is caused by the delegate being released. Apple does not recommend to retain delegates.
How can I check my delegate if is still valid before trying to send it a message?
If there's a chance that the delegate will get released by the setter, then there's something wrong with your design. You should only set delegates on objects that have a shorter lifespan than the delegate itself. For example, setting a delegate on a subview/controller is fine, because the subview/controller has a shorter lifespan than the caller.
AFAIK, there is no reliable way to detect if an object has been released already.
What Apple means about not retaining delegates is that objects should not retain their delegates because they don't own them. These are only objects that handle messages.
That doesn't mean that you shouldn't retain delegates at all. The object that creates the delegate needs to own it. In the context of non-GC apps this means it should handle the retain and release cycle, and for GC apps, it means that the controller object keeps hold of a pointer to the delegate in an iVar.
without seeing some code or the error message, it is hard to find the root of this problem.
In a photoviewer application I'm using asynchronous http to load images; it happens that the user often dismisses the current view (referenced by my async http object through a delegate) before the http download completed causing a BAD_ACCESS when calling the view controller delegate method. I solved this by setting the .delegate to nil inside the dealloc block of the view controller
I'd like to share my experience also, which is very similar to Nico's one.
I've been working with a modified example of LazyTablesCode, wich is an example that comes direcly from Apple and loads images in a UITableView asynchronously. Communication between the downloader and the view it's made via delegates.
In my code, I had the problem that sometimes the load of the image finishes when the form that should be called through the delegate has been released. I've been forced to add this piece of code inside the code of the viewController (dealloc method):
if (self.nsDictionaryWithObjectsDownloading != nil) {
for (id theKey in self.nsDictionaryWithObjectsDownloading) {
Myobj *downloader = [self.nsDictionaryWithObjectsDownloading objectForKey:theKey];
downloader.delegate = nil;
}
}
It seems that these lines are solving the problem. Anyway It would be very appreciated opinions about if it's a good solution or not or even about memory issues when doing downloader.delegate = nil;
Thanks and greetings,