美文网首页
iOS底层原理 - 探寻Runtime本质(四)

iOS底层原理 - 探寻Runtime本质(四)

作者: hazydream | 来源:发表于2018-07-24 10:00 被阅读12次

    面试题引发的思考:

    Q: 以下代码的打印内容是什么?

    // TODO: -----------------  Person类  -----------------
    @interface Person : NSObject
    @end
    
    @implementation Person
    @end
    
    // TODO: -----------------  Student类  -----------------
    @interface Student : Person
    @end
    
    @implementation Student
    - (instancetype)init
    {
        self = [super init];
        if (self) {
            NSLog(@"[self class] = %@", [self class]);
            NSLog(@"[self superclass] = %@", [self superclass]);
            NSLog(@"---------------------------");
            NSLog(@"[super class] = %@", [super class]);
            NSLog(@"[super superclass] = %@", [super superclass]);
        }
        return self;
    }
    @end
    
    // TODO: -----------------  main  -----------------
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Student *stu = [[Student alloc] init];
        }
        return 0;
    }
    

    打印结果应该是:

    [self class] = Student
    [self superclass] = Person
    ---------------------------
    [super class] = Person
    [super superclass] = NSObject
    

    真实打印结果是:

    真实打印结果

    Q:为什么打印结果和想象中有出入?

    • 关键在super关键字在调用方法时的底层调用流程。

    面试题一

    1> selfsuper

    // TODO: -----------------  Person类  -----------------
    @interface Person : NSObject
    - (void)run;
    @end
    
    @implementation Person
    - (void)run {
        NSLog(@"%s", __func__);
    }
    @end
    
    // TODO: -----------------  Student类  -----------------
    @interface Student : Person
    @end
    
    @implementation Student
    - (void)run {
        [super run];
        NSLog(@"Student - run");
    }
    @end
    

    通过xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Student.mStudent.m转化为C++代码查看其底层实现:

    OC代码转化为C++

    [super run];语句转化为C++代码:

    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){
    (id)self, 
    (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("run"));
    
    // 简化代码
    objc_msgSendSuper((__rw_objc_super){
            self,
            class_getSuperclass(objc_getClass("Student"))
        }, @selector(run));
    
    // 继续简化代码
    struct __rw_objc_super arg = {
            self,
            class_getSuperclass(objc_getClass("Student"))
        };
    objc_msgSendSuper(arg, @selector(run));
    

    由以上代码可知:

    • [super run];底层实现是objc_msgSendSuper函数;
    • objc_msgSendSuper函数传入两个参数:__rw_objc_super结构体与@selector(run)方法名;
    • __rw_objc_super结构体传入两个参数:selfclass_getSuperclass(objc_getClass("Student"))Student的父类Person

    OC源码中搜索objc_msgSendSuper

    objc_msgSendSuper函数

    由OC源码可知:
    objc_msgSendSuper函数传入的第一个参数为objc_super结构体;
    objc_super结构体传入两个参数:
    receiver消息接收者为self;
    superclass决定从哪一个类开始查找方法的实现。

    self、super调用方法流程

    由上图可知:
    super调用方法:消息接收者仍然是子类对象self,从父类开始查找方法的实现。

    2> classsuperclass

    classsuperclass的底层实现如下图所示:

    class与superclass的底层实现

    class方法的内部实现是根据消息接收者返回其对应的类对象。而class方法是在基类NSObject类中实现的。

    [self class];[super class];的消息接收者都是本类Student
    区别为:[self class];从本类类对象开始查找方法,[super class];从父类类对象开始查找方法。

    superclass方法的内部实现是根据消息接收者返回其对应的父类的类对象。

    [self superclass];[super superclass];的消息接收者都是父类Person
    区别为:[self superclass];从父类类对象开始查找方法,[super superclass];从父类的父类类对象开始查找方法。

    2. 面试题二

    isa、superclass指向图

    根据isasuperclass指向图,得到以下结论:

    isMemberOfClass与isKindOfClass的底层实现

    由以上源码可知:
    结论一:isMemberOfClass判断左边类型 是否等于 右边类型;
    结论二:isKindOfClass判断左边类型 是否等于 右边类型或其子类;
    结论三:左边(实例对象) 对应 右边(类对象);
    结论四:左边(类对象) 对应 右边(元类对象);
    结论五:基类的元类的superclass指向基类。

    // TODO: -----------------  Person类  -----------------
    @interface Person : NSObject
    @end
    
    @implementation Person
    @end
    
    // TODO: -----------------  main  -----------------
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
    
            Person *person = [[Person alloc] init];
            NSLog(@"%d", [person isMemberOfClass:[Person class]]); // 1,结论一、三
            NSLog(@"%d", [person isMemberOfClass:[NSObject class]]); // 0,结论一、三
            NSLog(@"%d", [person isKindOfClass:[Person class]]);  // 1,结论二、三
            NSLog(@"%d", [person isKindOfClass:[NSObject class]]);  // 1,结论二、三
    
            NSLog(@"%d", [[Person class] isMemberOfClass:[NSObject class]]); // 0,结论一、四
            NSLog(@"%d", [[NSObject class] isMemberOfClass:[NSObject class]]); // 0,结论一、四
            NSLog(@"%d", [[Person class] isKindOfClass:[NSObject class]]); // 1,结论二、四、五
            NSLog(@"%d", [[NSObject class] isKindOfClass:[NSObject class]]); // 1,结论二、四、五
        }
        return 0;
    }
    

    3. 面试题三

    // TODO: -----------------  Person类  -----------------
    @interface Person : NSObject
    @property (nonatomic, copy) NSString *name;
    - (void)print;
    @end
    
    @implementation Person
    - (void)print {
        NSLog(@"my name is %@", self.name);
    }
    @end
    
    // TODO: -----------------  ViewController类  -----------------
    
    QQ20190222-152850@2x.png QQ20190222-153008@2x.png

    super的本质、栈空间、消息机制、访问成员变量的本质

    相关文章

      网友评论

          本文标题:iOS底层原理 - 探寻Runtime本质(四)

          本文链接:https://www.haomeiwen.com/subject/vuhemftx.html