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

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

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

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

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

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

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

и следующие строки в конце, но в последнем блоке for

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 и allowWeakReferenc (соответственно загрузку и инициализацию) «из коробки»?

Ссылки: 1. Какао с любовью - Что такое метакласс в Objective-C?
2. Stack Overflow — ответ Джастина Шпар-Саммерса на вопрос "Как можно можно получить размер типа, для которого есть кодировка?"


person Nate Chandler    schedule 27.11.2011    source источник
comment
Странный! Являются ли IMP, которые вы получаете для этих методов, NULL?   -  person Jesse Rusak    schedule 27.11.2011
comment
К сожалению, IMP не равны NULL, и, кроме того, поскольку я собираюсь отредактировать вопрос, класс отвечает на все эти селекторы после того, как на нем вызывается objc_registerClassPair(). Кроме того, реализации этих четырех методов для MyNSObject отличаются от соответствующих реализаций для NSObject.   -  person Nate Chandler    schedule 27.11.2011
comment
Упс. Существует метод с именем (например, из четырех) инициализации для NSObject, реализация которого отличается от реализации инициализации для MyNSObject. Однако... см. ответ ниже.   -  person Nate Chandler    schedule 27.11.2011


Ответы (1)


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

method_getName: Method -> SEL

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

Однако очевидно, что это можно сделать своими руками. Следующий код демонстрирует это. Этот код получает все методы экземпляра в NSObject и выводит каждый из них с именем "retainWeakReference" или "allowsWeakReference", а затем получает все методы класса в NSObject< /strong> и распечатывает каждый из них с именем «инициализировать» или «загрузить».

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

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

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

person Nate Chandler    schedule 27.11.2011