Reputation: 6084
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.
Upvotes: 3
Views: 840
Reputation: 4553
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];
}
Upvotes: 0
Reputation: 1814
You will have to
object_getClass(rcv)
)class_getSuperclass(class)
)class_getMethodImplementation(superclass, sel)
)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.
Upvotes: 5
Reputation: 4176
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.
Upvotes: 0
Reputation: 6176
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
Upvotes: -2