当我使用objc运行时函数class_addMethod()
向NSObject的实例选择器注入一个实现时,它实际上将实现注入实例选择器和类选择器:
@implementation HelloWorldClass - (void) helloWorld{ NSLog(@"hello world from instance method -helloWorld"); } @end // ==== // Do method injection when the application did finish launching. Class sourceClass = objc_getClass("HelloWorldClass"); Class targetClass = objc_getClass("NSObject"); SEL helloWorldSelector = @selector(helloWorld); Method method = class_getInstanceMethod(sourceClass, helloWorldSelector); IMP imp = method_getImplementation(method); const char *methodTypeEncoding = method_getTypeEncoding(method); class_addMethod(targetClass, helloWorldSelector, imp, methodTypeEncoding);
现在我们只声明helloWorld
via Objc Category 的接口,并将helloWorld
消息调用到NSObject
实例和类:
// Declare the interface for `helloWorld @interface NSObject (HelloWorld) + (void) helloWorld; - (void) helloWorld; @end // Send the `helloWorld` message to NSObject class NSLog(@"Send the `helloWorld` message to NSObject class"); [NSObject helloWorld]; // Send the `helloWorld` message to NSObject instance NSLog(@"Send the `helloWorld` message to NSObject instance"); [[NSObject new] helloWorld];
虽然您只是将helloWorld
实现注入到NSObject实例选择器中class_addMethod()
,但是在注入之后解析了类和实例消息:
=> Send the `helloWorld` message to NSObject class => hello world from instance method -helloWorld => Send the `helloWorld` message to NSObject instance => hello world from instance method -helloWorld
经过测试,我发现class_addMethod()
只有当目标类为class_addMethod()
is 时才将实现添加到类和实例选择器中NSObject
.
它是objc-runtime还是Cocoa的bug?
不,这不是一个bug.它定义了运行时系统(虽然模糊不清)的行为.
就像每个实例都有一个isa
指向它的类的实例变量一样,内存中的每个类结构都有一个isa
指向其元类的成员.就像任何给定的类包含有关其实例的元数据一样 - 包括实例响应的方法列表 - 类的元类包含有关类本身的元数据,包括类响应的方法列表.
此外,每个类结构都有一个superclass
指向其超类的成员,该成员在元类层次结构中镜像(即,每个元类superclass
是另一个元类).
但是有一个主要的区别:NSObject
is 的超类是nil
,而NSObject
元类的超类是NSObject
.换句话说,NSObject
's元类继承了NSObject
实例方法.因此,Objective-C类不仅响应其定义的类方法,还响应NSObject
实例方法.
困惑了吗?Greg Parker撰写了一篇精彩的博客,其中包含一个非常有用的图表,说明了如何将它们连接在一起:
Hamster Emporium存档 - 类和元类
编辑
唉,互联网.如果您当前使用的浏览器不显示内嵌PDF文档,则此处是直接指向图表的链接:
Hamster Emporium档案 - 类图