在运行时dynamic实现委托

在我的类中,我有一个UIViewController的引用,并希望在运行时在此ViewController上实现委托。 委托只有一个方法(有两个参数),当ViewController上的委托方法被调用时,我的类应该处理这个调用。

我很确定这是可能的某种方法swizzling等,但我不知道如何做到这一点。

你想要什么是可能的,但这不是方法混合,因为你不想切换到方法,而是添加一个新的方法。 由于Objective-C的dynamic特性,它可以完成,但它仍然是一个肮脏的黑客,所以也向图书馆供应商提出function请求。

你想要的是class_addMethod()和一个C函数的实际实现。 还有一点,Objective-C方法 C方法,但是有两个隐式参数self_cmd ,必须记住(在创buildC方法时以及在告诉class_addMethod方法签名时都是这样。拉开这样的事情:

 #import <Foundation/Foundation.h> #import <objc/runtime.h> // Required for class_addMethod() @interface MyClass : NSObject @end @implementation MyClass @end @protocol MyProtocol <NSObject> - (void)printString:(NSString *)string; @end // Note the method signature containing the // two implicit parameters self and _cmd! void MyClassPrinStringIMP(id self, SEL _cmd, NSString *string) { NSLog(@"Hi I'm %@:%s and this is the string: %@", self, sel_getName(_cmd), string); } void PimpMyClass() { // The last argument is the signature. First character is the return type, in our case void // Then comes self and _cmd, followed by the NSString. You can use @encode() to find out how your // type is encoded. Best is to build this string at runtime, since the encoding can change with architectures class_addMethod([MyClass class], @selector(printString:), (IMP)MyClassPrinStringIMP, "v@:@"); } int main(int argc, const char * argv[]) { @autoreleasepool { PimpMyClass(); id foo = [[MyClass alloc] init]; // id, to silence the compiler! [foo printString:@"Hello World"]; } return 0; } 

示例输出:

 Hi I'm <MyClass: 0x100101810>:printString: and this is the string: Hello World 

编辑:你可能发现的东西是,在运行时检查传递的对象是否符合协议或不conformsToProtocol: conformsToProtocol:。 由于这个代码只是添加了方法实现,所以它仍然会失败,但是你可以告诉运行时你完全用这个函数调用来实现这个协议:

 class_addProtocol([MyClass class], @protocol(MyProtocol)); 

另类:代理

Objective-C的dynamic性和消息转发已经被@JasperBlues所赞扬,然而,在Objective-C中有一个特定的类被devise来做到这一点: NSProxy 。 它被devise为拦截发送的消息并dynamic地将其分发给相关目标,并且使用高级NSInvocation方法。 如果你可以像代理一样传递一个代理对象(取决于你的代码允许什么),创build一个NSProxy子类可能是最简单的方法。

但是,请注意,您最终会得到一个填充对象的填充对象,这个填充对象包含了自己的一些痛苦,当您试图通过->语法直接访问variables时将会中断对象。 这不是一个完全隐形的代理,但对大多数情况来说已经足够了。

首先,一些评论表明,你所要求的是“一件坏事”或“肮脏的黑客”。 我在这里不同意。 大多数现代的面向对象的语言都支持这些特性,并且它们被许多系统级的框架所使用。 当然,也许使用这些dynamicfunction的人性是不真正需要的(为了好玩或实践),即使更简单的方法也能正常工作。 小心这个。

Objective-C令人钦佩,因为它的某种遗留语言接近于“裸机”,但却具有令人惊讶的活力水平,使得在没有任何外部库或框架的情况下支持这些需求变得相对容易。

除了使用其他答案正确表示的class_addMethod指南之外,还有其他一些方法:

信息转发:(推荐)

所有的NSObject子类都有能力将他们无法响应的方法转发给另一个目标对象。 这与蹦床的底层概念类似。 苹果发布了使用这种方法的指南

使用转发调用的优点是它使用NSInvocation抽象级别,而不是直接调用C ObjC运行时API。 这将以下细节提取出来:

  • 结构和原语将被自动装箱/拆箱
  • 调度具有dynamic/未知数量的参数的方法变得容易。 直到arm64,这可以使用va_args完成,但是在arm64上,va_args可以直接复制到寄存器,而不会popup堆栈。

parsing实例方法:

实例方法是通过将C函数注册为实现来响应给定的消息来创build的。 这可以通过使用IMP_ImplementationWithBlock块来完成:

 + (BOOL)resolveInstanceMethod:(SEL)sel { IMP imp = imp_implementationWithBlock((__bridge id) objc_unretainedPointer( ^(id me, BOOL firstParam, NSString* secondParam) { //Implementation goes in here return something; //something of type 'id' })); class_addMethod(self, sel, imp, "@@:"); return YES; } return NO; } 

使用libffi:

Libffi也可以做这种事情,但是如果你使用的是纯粹的Objective-C,那么运行时已经有了这些function。