问题
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.
回答1:
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.
回答2:
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.
回答3:
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];
}
回答4:
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
来源:https://stackoverflow.com/questions/10328915/call-super-without-super-keyword