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

Во время выполнения создать копию MyNSObject * Class 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

Что здесь происходит?Реализуют ли классы (соответственно метаклассы) retainWeakReference и разрешаетWeakReferenc (соответственно загружать и инициализировать) «из коробки»?

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

1 Ответ

11 голосов
/ 27 ноября 2011

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

method_getName: Method -> SEL

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

Однако, очевидно, это можно сделать вручную.Следующий код демонстрирует это.Этот код получает все методы экземпляра в NSObject и распечатывает каждый из них с именем «retainWeakReference» или «allowWeakReference», а затем получает все методы class в 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);
    }
}

Вывод не соответствует ожидаемому, кроме предыдущего,:

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 , для которых существует несколько реализаций, что подтверждается тем фактом, что только четыре из них, о которых сообщается в коде в вопросе, не могут быть добавлены в MyNSObject .

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

...