Assume the following class hierarchy. Class A
is publicly declared:
@interface A : NSObject
+ (A)createInstance;
- (void)a;
@end
Class _B
is a private subclass of A
:
@interface _B : A
- (void)a;
- (void)b;
@end
Assume objects of class A
should only be created using the factory method createInstance
, which creates and returns an instance of _B
.
I want to enhance the functionality of an instance of A
on a per-instance basis. So I decided to do some ISA swizzling to achieve:
@interface ExtA : A
- (void)a;
@end
@implementation ExtA
- (void)a
{
NSLog("ExtA_a");
[super a];
}
@end
And I do the ISA swizzling using the following method on an NSObject
category (naive implementation shown here):
- (void)changeToSubclass:(Class)cls prefix:(NSString*)prefix suffix:(NSString*)suffix
{
NSString* className = [NSString stringWithFormat:@"%@%@%@", prefix ? prefix : @"", NSStringFromClass(object_getClass(self)), suffix ? suffix : @""];
if([className isEqualToString:NSStringFromClass(object_getClass(self))])
{
className = [NSString stringWithFormat:@"%@(%@)", NSStringFromClass(object_getClass(self)), NSStringFromClass(cls)];
}
Class newSubclass = objc_getClass(className.UTF8String);
if(newSubclass == nil)
{
newSubclass = objc_allocateClassPair(object_getClass(self), className.UTF8String, 0);
objc_registerClassPair(newSubclass);
unsigned int listCount = 0;
Method *list = class_copyMethodList(cls, &listCount);
for(int i = 0; i < listCount; i++)
{
class_addMethod(newSubclass, method_getName(list[i]), method_getImplementation(list[i]), method_getTypeEncoding(list[i]));
}
free(list);
listCount = 0;
list = class_copyMethodList(objc_getMetaClass(class_getName(cls)), &listCount);
for(int i = 0; i < listCount; i++)
{
class_addMethod(objc_getMetaClass(class_getName(newSubclass)), method_getName(list[i]), method_getImplementation(list[i]), method_getTypeEncoding(list[i]));
}
free(list);
}
object_setClass(self, newSubclass);
}
Everything seemingly works, but I noticed that [super a];
does not behave as expected, actually the implementation of -[A a]
is called, if if the superclass in runtime is actually _B
.
Replacing the call to super
with the following code works, but is ugly, and requires knowledge of and work by developers:
struct objc_super superInfo = {
self,
[self superclass]
};
objc_msgSendSuper(&superInfo, @selector(a));
What does the compiler emit when calling super
and any way to change this emitted code?