首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ISA swizzling和调用“Super”

ISA swizzling和调用“Super”
EN

Stack Overflow用户
提问于 2015-02-03 12:29:50
回答 1查看 244关注 0票数 1

假设以下类层次结构。A类是公开声明的:

代码语言:javascript
复制
@interface A : NSObject

+ (A)createInstance;
- (void)a;

@end

_BA的一个私有子类。

代码语言:javascript
复制
@interface _B : A

- (void)a;
- (void)b;

@end

假设类A的对象只应该使用工厂方法createInstance创建,该方法创建并返回_B的一个实例。

我想在每个实例的基础上增强A实例的功能。因此,我决定做一些ISA摆动,以实现:

代码语言:javascript
复制
@interface ExtA : A

- (void)a;

@end

@implementation ExtA

- (void)a
{
    NSLog("ExtA_a");
    [super a];
}

@end

我在NSObject类别上使用以下方法执行ISA (这里所示的简单实现):

代码语言:javascript
复制
- (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);
}

一切似乎都正常工作,但我注意到,如果运行时的超类实际上是[super a];,那么-[A a]的实现实际上是调用的,但我注意到它的行为并不像预期的那样。

用以下代码替换对super的调用是有效的,但很难看,并且需要开发人员了解和工作:

代码语言:javascript
复制
struct objc_super superInfo = {
    self,
    [self superclass]
};
objc_msgSendSuper(&superInfo, @selector(a));

编译器在调用super时会发出什么,以及任何改变发出的代码的方法?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-02-19 18:08:14

差别很小,但很重要。编译器发出一个函数调用,不是对objc_msgSendSuper,而是对objc_msgSendSuper2

你可能会问,有什么区别?虽然很小但很重要。

来自苹果的开源

代码语言:javascript
复制
/********************************************************************
 *
 * id objc_msgSendSuper(struct objc_super *super, SEL _cmd,...);
 *
 * struct objc_super {
 *      id  receiver;
 *      Class   class;
 * };
 ********************************************************************/

    ENTRY   _objc_msgSendSuper
    MESSENGER_START

// search the cache (objc_super in %a1)
    movq    class(%a1), %r11    // class = objc_super->class
    CacheLookup SUPER       // calls IMP on success

/* Snipped code for brevity */

/********************************************************************
 * id objc_msgSendSuper2
 ********************************************************************/

    ENTRY _objc_msgSendSuper2
    MESSENGER_START

    // objc_super->class is superclass of class to search

// search the cache (objc_super in %a1)
    movq    class(%a1), %r11    // cls = objc_super->class
    movq    8(%r11), %r11       // cls = class->superclass
    CacheLookup SUPER2      // calls IMP on success

对于那些不熟悉x86_64程序集的读者,重要的代码行如下:

代码语言:javascript
复制
movq    8(%r11), %r11       // cls = class->superclass

你可能会问,这是做什么的?这相当简单--而不是调用者传递超类进行搜索,而是由objc_msgSend实现执行。

但是,这一重要的区别导致了一个关键问题--在执行super调用时,它执行的是而不是调用[self class]。相反,它使用当前实现的类,当然是ExtA

因此,“修复”的唯一方法是在运行时更改ExtA的超类,这将导致您的方法按预期执行。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28299007

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档