Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can some methods (-retainWeakReference, -allowsWeakReference, +load, +initialize) on class NSObject not be added to other classes at runtime?

It is straightforward at runtime to create a copy MyNSObject of the Class NSObject:

First, create a new class pair.

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

Second read the methods, protocols, and ivars from NSObject and add them to the new class.

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

Third, read the methods from the metaclass of NSObject and add them to the new metaclass.

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

And finally, register the class pair.

objc_registerClassPair(MyNSObject);

Well, it's almost that straightforward. There are a couple of problems with this. Well, a couple of couples. If we were to add the following lines at the end but within the first for block

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

and the following lines at the end but within the last for block

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

Then we would see the following output:

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

What is going on here? Do classes (resp. metaclasses) implement retainWeakReference and allowsWeakReferenc (resp. load and initialize) "out of the box"?

References: 1. Cocoa with Love - What is a meta-class in Objective-C?
2. Stack Overflow - Justin Spahr-Summers response to "How can one obtain the sizeof a type for which one has an encoding?"

like image 713
Nate Chandler Avatar asked Nov 27 '11 03:11

Nate Chandler


People also ask

What is the difference between weak and unowned?

The weak reference is an optional type, which means weak reference will set to nil once the instance it refers to frees from memory. On the other hand, unowned reference is a non-optional type, it never will be set to nil and always have some value.

What is difference between strong and weak reference?

While strong references increase the retain count of an object by 1, weak references do not. In addition, weak references zero out the pointer to your object when it successfully deallocates. This ensures that when you access a weak reference, it will either be a valid object, or nil.

What is difference between strong and weak in IOS?

strong is the default. An object remains “alive” as long as there is a strong pointer to it. weak specifies a reference that does not keep the referenced object alive. A weak reference is set to nil when there are no strong references to the object.

What is weak strong and unowned in Swift?

Swift provides two ways to resolve strong reference cycles when you work with properties of class type: weak references and unowned references. Weak and unowned references enable one instance in a reference cycle to refer to the other instance without keeping a strong hold on it.


1 Answers

NSObject is an even more interesting beast than expected. Typically one thinks about the map

method_getName: Method -> SEL

as being one-to-one. I.e one usually thinks that method_getName(methodA) == method_getName(methodB) just in case methodA == methodB. One is encouraged to think this: one cannot create a class during coding via @interface which has multiple methods with the same selector, nor can one add two methods with the same selector to a class using class_addMethod() during runtime.

However, it is evidently possible to do it by hand. The following code demonstrates this. This code gets all the instance methods on NSObject and prints out each one named either "retainWeakReference" or "allowsWeakReference" and then gets all the class methods on NSObject and prints out each one named either "initialize" or "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);
    }
}

The output is not what one might, aside from the preceding build-up, have expected:

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)

So, as is now evident, NSObject has two implementations for each of the selectors -retainWeakReference, -allowsWeakReference, +load, and +initialize. These are the only four methods on NSObject for which there are multiple implementations, which is demonstrated by the fact that these were the only four reported by the code in the question as being unable to be added to MyNSObject.

A statement which gets close to counting as an answer to the question then is that you can't add multiple methods with the same selector to a class created at runtime via class_addMethod(). In particular, though, no, no methods are implemented by a class created at runtime with objc_allocateClassPair() "out of the box".

like image 167
Nate Chandler Avatar answered Sep 19 '22 00:09

Nate Chandler