I want' to implement "Fix and continue functionality" that was in Xcode 3.
CONTEXT:
The main idea is:
When I need to "Fix something fast", I'm not re-compiling, project. I'm compiling small Attacker class with 'updated' method implementation, loading it into memory and replacing VictimClass's method which have incorrect implementation in runtime.
I think that this method will work faster that full project recompilation.
When i'm done with fixes i'm just copying source of Attacker class method to Victim class.
PROBLEM
At the moment, I don't know how correctly call [super ...] in Attacker class.
For example, i have VictimClass
#interface VictimClass : UIView #end
#implementation VictimClass
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
}
#end
#interface AttackerClass : NSObject #end
#implementation AttackerClass
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
[self setupPrettyBackground];
}
#end
....
// EXCHANGE IMPLEMENTATIONS
Method m = class_getInstanceMethod([AttackerClass class], #selector(drawRect:));
const char * types = method_getTypeEncoding(m);
IMP attackerImp = method_getImplementation(m);
class_replaceMethod([VictimClass class], #selector(drawRect:), attackerImp, types);
// Invoking drawRect on Victim
VictimClass * view = /* */;
[view setNeedsDisplay];
At this point , when drawRect: method will be called, this will lead to exception, since drawRect: will be called on NSObject class, but not on UIView class
So, my question is, how correctly call [super drawRect:] in AttackerClass, to have possibility to correctly exchange implementation in runtime?
Main idea is to provide a way to correctly replace any method in Victim class by Attacker's class method. Generally, you don't know, superclass of Victim class.
UPDATE: Replacing implementation code added.
You will have to
get the receivers class (e.g. with object_getClass(rcv))
then get the super class of it (with class_getSuperclass(class))
then get the implementation of it (with class_getMethodImplementation(superclass, sel))
then call the imp.
done
Stop at any step if you got nil or NULL.
Oh, and all this seems silly. But I assume that the question just lacks of context to see the motivation for such a hack.
[Update]
An explanation for future readers:
The super keyword is resolved at compile time. Therefore it does not the intended thing when changing methods at runtime. A method which is intended to be injected in some object (and its class hierarchy) at runtime has to do super calls via runtime as outlined above.
Assuming that the runtime changes you're making involve modifying the superclass, you'll have to do something like this:
#implementation AttackerClass
-(void) drawRect:(CGRect)rect
{
if( [super respondsToSelector:#selector(drawRect:)] )
{
[super drawRect:rect];
}
[self setupPrettyBackground];
}
#end
This will check if the superclass "knows about" drawRect:, and doesn't call it in the case that super has no drawRect: selector.
Hence, when the superclass is NSObject the drawRect: message will not be sent. When you change it to UIView at runtime (whatever your reason for that is), the message can safely be sent.
One approach is to use objc_msgSendSuper. Your method -[AttackerClass drawRect:] will have the following implementation:
- (void)drawRect:(CGRect)rect {
struct objc_super superTarget;
superTarget.receiver = self;
superTarget.class = object_getClass(self);
objc_msgSendSuper(&superTarget, #selector(drawRect:), rect);
[self setupPrettyBackground];
}
but why do you need to call draw rect method for superclass NSObject, when NSObject hasn't got that method? just don't do it... call it just in VictimClass drawrect
Related
This question already has answers here:
NSInvocation for Dummies?
(4 answers)
Closed 7 years ago.
I came across this code in a project, where it is using NSInvocation. I want to know what it is supposed to do, and why would we ever need that. Simple explanations would be appreciated. I am posting the code.
// Public interface
#interface CCDelegateSplitter : NSObject
- (void) addDelegate: (id) delegate;
- (void) addDelegates: (NSArray*) delegates;
#end
// Private interface
#interface CCDelegateSplitter ()
#property(strong) NSMutableSet *delegates;
#end
#implementation CCDelegateSplitter
- (id) init
{
self = [super init];
_delegates = [NSMutableSet set];
return self;
}
- (void) addDelegate: (id) delegate
{
[_delegates addObject:delegate];
}
- (void) addDelegates: (NSArray*) delegates
{
[_delegates addObjectsFromArray:delegates];
}
- (void) forwardInvocation: (NSInvocation*) invocation
{
for (id delegate in _delegates) {
[invocation invokeWithTarget:delegate];
}
}
- (NSMethodSignature*) methodSignatureForSelector: (SEL) selector
{
NSMethodSignature *our = [super methodSignatureForSelector:selector];
NSMethodSignature *delegated = [(NSObject *)[_delegates anyObject] methodSignatureForSelector:selector];
return our ? our : delegated;
}
- (BOOL) respondsToSelector: (SEL) selector
{
return [[_delegates anyObject] respondsToSelector:selector];
}
#end
I'll assume you know what an NSInvocation is (if not, it's a data structure that holds all the information needed to make a method call; think "blocks" from long before blocks were added to the language).
forwardInvocation: is one of the methods that the runtime will call if it cannot find an implementation for a method. So if you pass a -doSomething message to an object [object doSomething], it will first check whether it has a doSomething method. Then it will check its superclasses. It'll try dynamic method resolution (resolveInstanceMethod for instance). It'll look for a forwarding target (forwardingTargetForSelector:), and it'll finally, if everything else fails, it'll create an invocation (using methodSignatureForSelector: and punt to forwardInvocation:. By default, forwardInvocation: just calls doesNotRecognizeSelector: which crashes you on iOS (or terminates the current thread on OS X). But you can override it to do something else (like they have here).
methodSignatureForSelector: is necessary so that the runtime system can create an invocation out of a message. This one either returns a method signature from this object or its superclasses, or it asks one of its targets for the appropriate method signature. A selector by itself isn't enough to figure out exactly how to call a method. The system needs to ask an object how the method actually works (what types it takes and what type it returns).
The code you've posted is a multi-delegate trampoline. It will accept any selector that its targets respond to (technically it'll pick a random target and see if it responds), and it'll forward that message to all of its targets.
For a similar trampoline with some comments about usage, you may want to look at RNObserverManager.
Take a look at the link in bfitch's comment. That covers what NSInvocation is, and hints at why you would use it, but doesn't cover the why in much detail.
NSInvocation lets you do fairly advanced things like creating a proxy object that actually forwards messages to another object. With NSInvocation it's possible to take ANY message at runtime and forward it to another object.
Another example is the performSelector family of methods. There's performSelector:, performSelector:withObject:, and performSelector:withObject:withObject:. (plus variants like performSelector:withObject:afterDelay:, performSelector:onThread:, etc.) Those methods take 0, 1, or or 2 objects as parameters. If you need to invoke a method on another object that takes scalar parameters, or anything other than 0, 1 or 2 objects, you're out of luck. However you can send messages with ANY type of parameters using NSInvocation.
Note that when blocks were added to Objective-C the need for tricks like performSelector and NSInvocation become less. Blocks can reference variables from their enclosing scope, which makes them more flexible.
I would like to set a member variable in a derived object before i call [super init].
All I can find is that you should not do such a thing. My worakround, to do it anyhow, works, but actually I like to know what the consequences are when bending the rules. Or even better if there is a correct way to deal with this.
The Details:
I have several wrappers that bind a c++ object to an objective-c objec (mostly UI...View or UI...Controller)
#interface my_scrollview : UIScrollView
{
my_c_class* m_p;
}
-(id) initWithFrame:(CGRect)frame wrapper: (my_scrollview*) pWrap;
-(void) setContentOffset:(CGPoint)contentOffset;
#end
#implementation dwin_scrollview_ios
-(id) initWithFrame:(CGRect)frame wrapper: (my_scrollview*) pWrap
{
m_p = pWrap; // illegal but works?
return [super initWithFrame: frame];
//m_p = pWrap; // to late because [super init...] already called overriden func.
}
In my overwritten setContentOffset-method I need to access my C++-Object.
The Problem arises because the initWithFrame internally initializes its content using setContentOffset. So this method is called before I could "legaly" set up the link to my c++-object.
I can implement my overrides with a check if m_p is set(luckily it's initialized to nil). But I have to synchronize the state of the view and my c++-object after the the init-method. In this example this is no big deal but other such realtions are much more complicated and I end up with lot of code that repeats steps of the initialization or otherwise brings me back in sync, although before the [super init...] I know I was in sync.
Is there a pattern to solve this correct (and elegant)?
Is it really so bad to int the pointer before the call to [super init..];?
(I assume one consequence is that this crashes if [super init] returns nil...? any other cases?)
Thanks in advance
Moritz
There is nothing magical about init methods in Objective-C. alloc returns an object of the class that you want, with all instance variables initialized to 0 / nil / NULL / 0.0 etc. Each init method then just executes the code that the developer has written.
There are things that are obviously stupid, like setting an instance variable of the superclass, then calling [super init] which promptly overwrites it. And you need to be aware that init doesn't necessarily return self, but a different object, in which case everything you've initialised in the base class before calling [super init] will be gone.
// illegal but works?
No, it's not illegal. It's perfectly legitimate, although unconventional, to do stuff to an object before its superclass' initializer has been run. It may still lead to unexpected behavior and bugs, but it's not illegal per se. Sometimes it's even necessary, for example when you want to perform some computation and delegate the result of that computation to the superclass' initializer.
You are using wrong init
try this:
-(id) initWithFrame:(CGRect)frame wrapper: (my_scrollview*) pWrap
{
self = [super initWithFrame: frame];
if (self) {
m_p = pWrap;
}
return self;
}
I have the following method:
- (void) someMethod
{
if ([super respondsToSelector:#selector(someMethod)])
{
[super performSelector:#selector(someMethod)
withObject:nil];
}
}
someMethod does not exist on superclass. as i understand, if there is no such method, runtime will ask the next responder in chain for such method till the NSObject class. And i was sure, that if statement will return NO.
Statement return YES. After that it performs selector without crash. As result - infinite recursion.
so, i have two questions:
Why [super respondsToSelector:#selector(someMethod)] returns YES ?
Why [super performSelector:#selector(someMethod) withObject:nil] does not crash with error 'does not responds to selector' ?
I think i've missed something essential.
Please, help.
Yes, you missed something essential as you suggest. From the documentation for respondsToSelector:
You cannot test whether an object inherits a method from its superclass by sending respondsToSelector: to the object using the super keyword. This method will still be testing the object as a whole, not just the superclass’s implementation. Therefore, sending respondsToSelector: to super is equivalent to sending it to self. Instead, you must invoke the NSObject class method instancesRespondToSelector: directly on the object’s superclass, as illustrated in the following code fragment.
if( [MySuperclass instancesRespondToSelector:#selector(aMethod)] )
{
// invoke the inherited method
[super aMethod];
}
HTH
I'm using #implementation to add a new function to UIView.
#implementation UIView (test)
- (void)newFunction {
}
#end
Now, in the newFunction I want to "grab" the object (UIView) that was used when calling the function.
For example when I call newFunction within viewDidLoad
- (void)viewDidLoad {
[myView newFunction];
}
I want the newFunction to know what object was used to make the call (in this case, myView).
A simple solution would be to pass it along when making the call ([myView newFunction:myView]), but that is not what I am looking for.
I looked at Apple's documentation on the subject, but didn't really find an answer to my question.
#import "UIView+UIView_Category.h"
#implementation UIView (UIView_Category)
- (void)newFunction
{
NSLog(#"Object = %#",self);
}
#end
What you describe is called a category (not #implementation). It is an extension to the UIView class (in this case).
Generalcally:
#implementation __CLASS_TO_EXTEND__ (__CATEGORY_NAME__)
The category, as it is an extension, is the instance that you call the method on. So, you use self as you usually would to access the current instance.
I have a small question on what super means in xcode. For instance I have the following code. Would it work if i said [super dealloc] first in the dealloc method? Or should super always come last? What exactly is super - I know its the super class but is it the parent class or?
This is the .h file for this class
#interface TwitterTableViewController : PullRefreshTableViewController<TweetDelegate>
This is some code from the .m class for the above interface
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)dealloc
{
[self.twitterManager release];
[tweets release];
[lastRefreshedLabel release];
[super dealloc];
}
I have checked all over and dont have a satisfactory knowledge yet. If anyone can explain this in laymans terms would be best. thanks
super calls the implementation of the super-class. In your case this would be PullRefreshTableViewController.
Regarding if you should call super before/after your own code, it really depends on what you want to achieve by overriding the method.
In case of dealloc have a look at this question, as Nick Bull mentioned.
The super class can be regarded as a "parent class", yes, if you mean. [super dealloc] calls the implementation of the dealloc method of the super/parent class of the current object (or class, if you call it from a class method).
But, there's no such rule that "super must always come last". The reason why dealloc must always come last is that it destroys the object, so if you accessed your object after it returned, that would crash.
In addition to the existing answers here, the info in this answer may help you in deciding when to call "super".
The methods that are called when the child object/view/viewcontroller is created/initialized, the first thing you do is call the super (i.e., if you want to call it).
When the child object/view/viewcontroller is destroyed/removed, you call the super at the end of the method.
eg:
// methods called when the you are loading/showing with the view controller
-(void)viewDidLoad {
[super viewDidLoad];
//call super and then all your code goes here
}
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
//call super and then all your code goes here
}
// methods called when the you are done with the view controller
- (void)viewWillDisappear:(BOOL)animated{
//all your code goes here and then call super
[super viewWillDisappear:animated];
}
- (void)viewDidUnload {
//all your code goes here and then call super
[super viewDidUnload];
}
Similarly, for -dealloc, the super is called at the end, for -init super is called at the beginning.
Hope this demystifies Super.
super stands for the super object / aka the father of the class you are impementing which extends
in your case is a PullRefreshTableViewController
this kind of behavior is the base of all the object oriented languages, like obj-c.
in the case you are analyzing the super class has a method called
-(UITableviewStyle *) initWithStyle:style
super specifies that the messages should be sent to the parent class (superclass) of the current class. Since you're inheriting from PullRefreshTableViewController, that's where they'll get sent.
super is the parent class, here PullRefreshTableViewController. Calling [super dealloc], which you should always do if you need to implement your own dealloc method, allows your parent class (and its parent class, and so on) to clean up after themselves. It's usually a good idea to perform your own releases, and only then let the parent class do the same, because you can never be sure what the parent class will sweep away from under your feet.