Call super without “super” keyword

别说谁变了你拦得住时间么 提交于 2019-12-22 09:50:14

问题


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

  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.




回答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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!