Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Override a method in a single object instance

Am not sure how to put this, and I couldn't find the answer because of my inability to find the words to express what am looking for. (!)

In Java, I used to do something like this (I don't remember):

JPanel myButton = new JPanel("Press me"){
    public void add(JComponent component){
        //override add method
    }
};

But, i couldn't find how to do this in Objective-C .. What I found in my search was categories and weird ^{} symbols ...

So, how can I override method(s) in a newly created object?

(For example, override -(BOOL)isEqual; in a newly created NSString* ?)

Am sorry if the question is a bit vague..

EDIT:

Obviously, without subclassing :)

EDIT:

Might as well post my problem in case someone has a better idea:

I have a few CCTransitions in COCOS2D, and I want to be notified when the transition ends .. The thing is, as soon as the transition ends, the -(void)finish; method is invoked (which is part of the CCTransition class structure)

I would really want to avoid subclassing the CCTransition class, and override the finish method to do my logic when the transition ends :)

EDIT:

-(void)onEnterTransitionDidFinish; ... I can't believe something as awesome as that existed and I haven't came across it while searching......

Which means, instead of subclassing CCTransition, override this method in my CCNode subclass :D!

like image 496
Mazyod Avatar asked Jun 02 '11 11:06

Mazyod


2 Answers

It's still not going to be very clean, but assuming you're willing to concentrate the ugliness, you could do something like (untested):

Method methodToReplace =
          [targetClass instanceMethodSignatureForSelector:@selector(methodToReplace)];
IMP implementationToSet =
          [someProxyClass instanceMethodForSelector:@selector(implementationYouWant)];

method_setImplementation(methodToReplace, implementationToSet);

Relevant reference documentation is the Objective-C Runtime Reference and, optionally, the NSObject Class Reference (because it makes a few things slightly neater, though e.g. you could use class_getInstanceMethod from the runtime rather than instanceMethodSigntureForSelector:).

Note that you'll have no way to call the original implementation if you use exactly that recipe. method_setImplementation returns the old implementation, it's generally wise to add that to the class under a brand new selector and call that instead.

For reference, I've had a legitimate reason to do this sort of thing only exactly once: when we implemented printing support in an iOS application with which needed to be compatible with both OS 3.2 and 4.0. You need to subclass a particular class, but the class isn't available in 3.2. So you sort of have to subclass at runtime (though the conceptually neater way would be to use a normal subclass, put that into a framework and weak link, but Apple's iOS SDK terms allow static libraries only, so...).

like image 185
Tommy Avatar answered Sep 30 '22 07:09

Tommy


Following Daniel's suggestion, you can implement a method in an NSObject category of the form

[anObject overrideMethod:@selector(foo:) 
          byBlock:^(id self,id super,id originalArg){
          ...
}];

What you need to do is to

  1. objc_allocateClassPair against self's own class, to create a new temporary class
  2. Turn a block into a function pointer, using e.g. this or this
  3. method_setImplementation to set the new implementation to the temporary class
  4. use object_setClass to self to set the class to the new temporary class
  5. I haven't figured out how to provide super to the block :p

It's believed this is basically how the KVO is done by Apple, see e.g. this discussion.

Read Runtime reference.

like image 21
Yuji Avatar answered Sep 30 '22 07:09

Yuji