为什么类NSObject上的某些方法(-retainWeakReference,-allowsWeakReference,+ load,+ initialize)不能在运行时添加到其他类?

Nat*_*ler 14 objective-c objective-c-runtime

它是在运行时简单的创建副本MyNSObject中的 NSObject的:

首先,创建一个新的类对.

Class MyNSObject = objc_allocateClassPair(nil, "MyNSObject", 0);
Run Code Online (Sandbox Code Playgroud)

第二步从NSObject读取方法,协议和ivars ,并将它们添加到新类中.

uint instanceMethodCount;
Method *instanceMethodArray = class_copyMethodList([NSObject class], &instanceMethodCount);
for (int i = 0; i < instanceMethodCount; i++) {
    Method method = *(instanceMethodArray + i);
    SEL selector =  method_getName(method);
    IMP implementation = method_getImplementation(method);
    const char *types = method_getTypeEncoding(method);
    BOOL success = class_addMethod(MyNSObject, selector, implementation, types);
}
free(instanceMethodArray);

uint protocolCount;
Protocol **protocolArray = class_copyProtocolList([NSObject class], &protocolCount);
for (int i = 0; i < protocolCount; i++) {
    Protocol *protocol = *(protocolArray + i);
    BOOL success = class_addProtocol(MyNSObject, protocol);
}
free(protocolArray);

uint ivarCount;
Ivar *ivarArray = class_copyIvarList([NSObject class], &ivarCount);
for (int i = 0; i < ivarCount; i++) {
    Ivar ivar = *(ivarArray + i);
    const char *name = ivar_getName(ivar);
    const char *typeEncoding = ivar_getTypeEncoding(ivar);
    NSUInteger size, alignment;
    NSGetSizeAndAlignment(typeEncoding, &size, &alignment);
    BOOL success = class_addIvar(MyNSObject, name, size, alignment, typeEncoding);
}
free (ivarArray);
Run Code Online (Sandbox Code Playgroud)

第三,从NSObject的元类中读取方法并将它们添加到新的元类中.

uint classMethodCount;
Method *classMethodArray = class_copyMethodList(object_getClass([NSObject class]), &classMethodCount);
for (int i = 0; i < classMethodCount; i++) {
Method method = *(classMethodArray + i);
SEL selector =  method_getName(method);
IMP implementation = method_getImplementation(method);
const char *types = method_getTypeEncoding(method);
BOOL success = class_addMethod(object_getClass(MyNSObject), selector, implementation, types);
}
free(classMethodArray);
Run Code Online (Sandbox Code Playgroud)

最后,注册班级对.

objc_registerClassPair(MyNSObject);
Run Code Online (Sandbox Code Playgroud)

嗯,这几乎是那么简单.这有几个问题.好吧,几对情侣.如果我们在末尾添加以下行,但是在第一个块中添加

if (!success) {
    NSLog(@"unable to add method with selector named %@ to class MyNSObject", NSStringFromSelector(selector));
}
Run Code Online (Sandbox Code Playgroud)

以及后面的行,但是在最后一行中

if (!success) {
    NSLog(@"unable to add method with selector name %@ to metaclass MyNSObject", NSStringFromSelector(selector));
}
Run Code Online (Sandbox Code Playgroud)

然后我们会看到以下输出:

unable to add method with selector name retainWeakReference to class MyNSObject
unable to add method with selector name allowsWeakReference to class MyNSObject
unable to add method with selector name load to metaclass MyNSObject
unable to add method with selector name initialize to metaclass MyNSObject
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?类(resp.emet类)是否实现了retainWeakReference并允许WeakReferenc(resp.加载和初始化)"开箱即用"?

参考文献: 1.Cocoa with Love - Objective-C中的元类是什么?
2. Stack Overflow - Justin Spahr-Summers回应"如何获得一个具有编码的类型的大小?"

Nat*_*ler 11

NSObject是一个比预期更有趣的野兽.通常人们会考虑地图

method_getName: Method -> SEL
Run Code Online (Sandbox Code Playgroud)

作为一对一.即通常认为method_getName(methodA) == method_getName(methodB)以防万一methodA == methodB.我们鼓励人们这样想:在编码期间不能创建一个@interface具有相同选择器的多个方法的类,也不能class_addMethod()在运行时使用相同的选择器将两个方法添加到类中.

但是,显然可以手工完成.以下代码演示了这一点.此代码获取NSObject上的所有实例方法,并打印出名为"retainWeakReference"或"allowsWeakReference"的每个方法,然后获取NSObject上的所有方法,并打印出名为"initialize"或"load"的每个方法.

uint NSObjectInstanceMethodCount;
Method *NSObjectInstanceMethodArray = class_copyMethodList([NSObject class], &NSObjectInstanceMethodCount);
for (int i = 0; i < NSObjectInstanceMethodCount; i++) {
    Method method = *(NSObjectInstanceMethodArray + i);
    SEL selector = method_getName(method);
    IMP implementation = method_getImplementation(method);
    const char *types = method_getTypeEncoding(method);
    if (strcmp(selector, "retainWeakReference") == 0 || strcmp(selector, "allowsWeakReference") == 0) {
        NSLog(@"NSObject implements method(%s,%p,%s)", selector, implementation, types);
    }
}

uint NSObjectClassMethodCount;
Method *NSObjectClassMethodArray = class_copyMethodList(object_getClass([NSObject class]), &NSObjectClassMethodCount);
for (int i = 0; i < NSObjectClassMethodCount; i++) {
    Method method = *(NSObjectClassMethodArray + i);
    SEL selector = method_getName(method);
    IMP implementation = method_getImplementation(method);
    const char *types = method_getTypeEncoding(method);
    if (strcmp(selector, "initialize") == 0 || strcmp(selector, "load") == 0) {
        NSLog(@"metaNSObject implements method(%s,%p,%s)", selector, implementation, types);
    }
}
Run Code Online (Sandbox Code Playgroud)

除了前面的构建之外,输出不是预期的:

NSObject implements method(retainWeakReference,0x7fff8a120b1f,c16@0:8)
NSObject implements method(allowsWeakReference,0x7fff8a120b05,c16@0:8)
NSObject implements method(retainWeakReference,0x7fff80ad6db0,c16@0:8)
NSObject implements method(allowsWeakReference,0x7fff80ad6d90,c16@0:8)
metaNSObject implements method(load,0x7fff8a09e4f2,v16@0:8)
metaNSObject implements method(initialize,0x7fff8a00cb89,v16@0:8)
metaNSObject implements method(load,0x7fff80a57670,v16@0:8)
metaNSObject implements method(initialize,0x7fff80a133d0,v16@0:8)
Run Code Online (Sandbox Code Playgroud)

所以,现在很明显,NSObject的对每个选择的两种实现方式-retainWeakReference,-allowsWeakReference,+load,和+initialize.这是NSObject上唯一有四种方法,其中有多种实现,这是由于问题中的代码只能报告为无法添加到MyNSObject中的四种方法.

接近计算作为问题答案的语句是,您不能将具有相同选择器的多个方法添加到在运行时创建的类中class_addMethod().特别是,不,没有方法是在运行时使用objc_allocateClassPair()"开箱即用" 创建的类.