Почему некоторые методы (-retainWeakReference, -allowsWeakReference, + load, + initialize) в классе NSObject не добавляются в другие классы во время выполнения?

Во время выполнения просто создать копию MyNSObject класса NSObject:

Сначала создайте новую пару .

Class MyNSObject = objc_allocateClassPair(nil, "MyNSObject", 0);

Второе прочитайте методы, протоколы и ivars из NSObject и добавьте их в новый класс.

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);

В-третьих, прочитайте методы из метакласса 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);

И, наконец, зарегистрируйте пару классов.

objc_registerClassPair(MyNSObject);

Ну, это почти так просто. Есть пара проблем с этим. Ну, пара пар. Если бы мы добавили следующие строки в конец, но в пределах первого для блока

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

и следующие строки в конце, но в течение последнего для блока

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

Затем мы увидим следующий вывод:

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

Что здесь происходит? Выполняют ли классы (соответственно метаклассы) keepWeakReference и позволяют WeakReferenc (соответственно загрузить и инициализировать) "из коробки"?

Литература: 1. Cocoa с любовью - Что такое метакласс в Objective-C?
2. Переполнение стека - ответ Justin Spahr-Summers на "Как получить размер sizeof, для которого есть кодировка?"

Ответы

Ответ 1

NSObject - еще более интересный зверь, чем ожидалось. Обычно мы думаем о карте

method_getName: Method -> SEL

как один к одному. Обычно считается, что method_getName(methodA) == method_getName(methodB) на всякий случай methodA == methodB. Можно подумать об этом: нельзя создавать класс во время кодирования через @interface, который имеет несколько методов с одним и тем же селектором, а также не может добавить два метода с тем же селектором в класс, используя class_addMethod() во время выполнения.

Однако, очевидно, это возможно сделать вручную. Следующий код демонстрирует это. Этот код получает все методы экземпляра в NSObject и печатает каждый из них с именем "preserveWeakReference" или "allowWeakReference", а затем получает все методы класса в NSObject и печатает каждый один из них называется "инициализировать" или "загрузить".

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);
    }
}

Результат не тот, который можно было бы, помимо предыдущего наращивания, ожидать:

NSObject implements method(retainWeakReference,0x7fff8a120b1f,[email protected]:8)
NSObject implements method(allowsWeakReference,0x7fff8a120b05,[email protected]:8)
NSObject implements method(retainWeakReference,0x7fff80ad6db0,[email protected]:8)
NSObject implements method(allowsWeakReference,0x7fff80ad6d90,[email protected]:8)
metaNSObject implements method(load,0x7fff8a09e4f2,[email protected]:8)
metaNSObject implements method(initialize,0x7fff8a00cb89,[email protected]:8)
metaNSObject implements method(load,0x7fff80a57670,[email protected]:8)
metaNSObject implements method(initialize,0x7fff80a133d0,[email protected]:8)

Итак, как теперь очевидно, NSObject имеет две реализации для каждого из селекторов -retainWeakReference, -allowsWeakReference, +load и +initialize. Это единственные четыре метода в NSObject, для которых существует несколько реализаций, что подтверждается тем фактом, что они были единственными четырьмя, указанными в коде в вопросе, поскольку они не могут быть добавлены к MyNSObject.

Утверждение, которое приближается к подсчету в качестве ответа на вопрос, заключается в том, что вы не можете добавлять несколько методов с тем же селектором в класс, созданный во время выполнения через class_addMethod(). В частности, однако, нет, никакие методы не реализуются классом, созданным во время выполнения, с objc_allocateClassPair() "из коробки".