3

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.

AstroCB
  • 12,337
  • 20
  • 57
  • 73
tt.Kilew
  • 5,954
  • 2
  • 33
  • 51
  • 1
    still don't understand, what is Runtime replace method. – Amitg2k12 Apr 26 '12 at 07:29
  • 1
    Why do you change the superclass of AttackerClass from NSObject to UIView? – Jonathan Naguin Apr 26 '12 at 07:31
  • 1
    he doesn't... he just has 2 separated classes – meronix Apr 26 '12 at 07:40
  • i love how you use *attacker class* and *victim class* in your question.. very nice analogy to simplify a relatively complex scenario – abbood Oct 09 '14 at 04:29
  • do you know how to do the above without falling into a recursive call of super? see the update to my question [here](http://stackoverflow.com/questions/26258071/how-to-override-swizzle-a-method-of-a-private-class-in-runtime-objective-c) – abbood Oct 09 '14 at 05:46

4 Answers4

5

You will have to

  1. get the receivers class (e.g. with object_getClass(rcv))
  2. then get the super class of it (with class_getSuperclass(class))
  3. then get the implementation of it (with class_getMethodImplementation(superclass, sel))
  4. 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.

Tilo Prütz
  • 1,766
  • 3
  • 16
  • 27
  • Hm... I'll try to do this. And tell you result. this is possibly the correct answer. – tt.Kilew Apr 26 '12 at 09:31
  • Thanks, man, by using this behaviour, you actually can emulate "super" call – tt.Kilew Apr 26 '12 at 20:03
  • See also [Mike Ash's Friday Q&A on the topic](http://www.mikeash.com/pyblog/friday-qa-2010-11-19-creating-classes-at-runtime-for-fun-and-profit.html). – Adam Sharp Jul 01 '13 at 10:05
0

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.

inspector-g
  • 4,146
  • 2
  • 24
  • 33
  • 1
    you're right... but it's a true fact that when you write your code you already know that your superclass hasn't got that method... no need to call it and no need to check... – meronix Apr 26 '12 at 07:38
  • @meronix agreed, but he implied that he is changing the superclass at runtime and doesn't want to send the `drawRect:` message until the superclass has been set. – inspector-g Apr 26 '12 at 07:39
  • I'm still need to call that super... method implementation. Your way is just to skip calling super. But i need to call it same way, like it was before method replacement. – tt.Kilew Apr 26 '12 at 08:40
  • That doesn't make any sense. You cannot send a message to a superclass that doesn't respond to said message and expect it to work. The only way to avoid an exception at runtime is **not** send the message to the superclass in the first place. If you're replacing a method instead of replacing the superclass, this is still safe and the proper thing to do. – inspector-g Apr 26 '12 at 15:28
0

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];
}
Nate Chandler
  • 4,533
  • 1
  • 23
  • 32
  • btw i used your answer [here](http://stackoverflow.com/a/26272294/766570).. worked like a charm! :) – abbood Oct 09 '14 at 14:49
-2

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

meronix
  • 6,175
  • 1
  • 23
  • 36