美文网首页Runtime & Runloop
Runtime:super原理分析

Runtime:super原理分析

作者: 码小菜 | 来源:发表于2020-02-27 12:35 被阅读0次
夜景

目录
一,objc_msgSendSuper
二,方法和内存地址查找

一,objc_msgSendSuper

1,实例代码

// Person
@interface Person : NSObject
@end

@implementation Person
@end

// Student
@interface Student : Person
@end

@implementation Student
- (instancetype)init {
    self = [super init];
    if (self) {
        NSLog(@"%@", [self class]);
        NSLog(@"%@", [self superclass]);
        NSLog(@"%@", [super class]);
        NSLog(@"%@", [super superclass]);
    }
    return self;
}
@end

// 使用
int main(int argc, char * argv[]) {
    @autoreleasepool {
        Student *student = [Student new];
    }
    return 0;
}

// 打印
Student
Person
Student
Person

2,底层代码(用clang进行转换)

((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"));
((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("superclass"));
((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("class"));
((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("superclass"));

// 简化代码
objc_msgSend(self, @selector(class));
objc_msgSend(self, @selector(superclass));
objc_msgSendSuper({self, [Person class]}, @selector(class));
objc_msgSendSuper({self, [Person class]}, @selector(superclass));

3,说明(源码下载地址

  • objc_msgSend
// 第一个参数是消息接收者,第二个参数是方法的SEL
OBJC_EXPORT id _Nullable
objc_msgSend(id _Nullable self, SEL _Nonnull op, ...);
  • objc_msgSendSuper
// 第一个参数是objc_super,第二个参数是方法的SEL
OBJC_EXPORT id _Nullable 
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...);

struct objc_super {
    __unsafe_unretained _Nonnull id receiver;       // 消息接收者
    __unsafe_unretained _Nonnull Class super_class; // 消息接收者的父类
};
  • class
- (Class)class {
    // 返回self所属的类
    return object_getClass(self);
}
  • superclass
- (Class)superclass {
    // 返回self所属类的父类
    return [self class]->superclass;
}

4,分析

  • self调用方法会转换为objc_msgSend函数的调用,用super调用方法会转换为objc_msgSendSuper函数的调用

  • objc_msgSendobjc_msgSendSuper的相同点:消息接收者都是self

classsuperclass方法内部只用到了消息接收者,所以用self和用super调用这两个方法的结果是一样的

  • objc_msgSendobjc_msgSendSuper的不同点:objc_msgSend是从消息接收者所属的类开始查找方法的,而objc_msgSendSuper是从消息接收者的父类开始查找方法的(这就是super_class存在的作用)

1>classsuperclass方法是由NSObject实现的
2>蓝色是objc_msgSend查找方法的过程
3>红色是objc_msgSendSuper查找方法的过程

查找方法
二,方法和内存地址查找

1,实例代码

// Person
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
- (void)print;
@end

@implementation Person
- (void)print {
    NSLog(@"my name is %@", _name);
}
@end

// 使用
- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSString *string = @"111";
    id cls = [Person class];
    void *obj = &cls;
    [(__bridge id)obj print];
}

// 打印
my name is 111

2,问题一:obj不是Person对象,为何调用print方法能成功?

  • 对比代码
// 使用
- (void)viewDidLoad {
    [super viewDidLoad];

    Person *person = [Person new];
    person.name = @"222";
    [person print];
}

// 打印
my name is 222
  • 图解
结构
  • 分析

1>person先通过isa找到Person的class对象,然后在class对象中找到print方法并进行调用
2>同理,obj通过cls也可以找到Person的class对象,所以也能找到print方法并进行调用

3,问题二:为何_name的值是111?

  • 局部变量

1>局部变量存储在栈区
2>栈区是从高地址往低地址进行分配的

  • stringclsobj都是局部变量,各占八个字节

1>打印

NSLog(@"%p---%p---%p", &string, &cls, &obj);

// 打印
0x7ffeef3ef0d8---0x7ffeef3ef0d0---0x7ffeef3ef0c8

2>图解

局部变量
  • 访问_name

1>Person对象是一个结构体,结构体成员的地址是从低到高依次排列的,所以_name的地址高于isa,另外string的地址高于cls
2>person访问_name时会先跳过isa,同理,obj访问_name时会先跳过cls,但cls后面存储的是string,所以把string当做_name来使用了

访问_name

4,问题三:为何_name的值又是ViewController对象?

  • 实例代码
// 使用
- (void)viewDidLoad {
    [super viewDidLoad];

    id cls = [Person class];
    void *obj = &cls;
    [(__bridge id)obj print];
}

// 打印
my name is <ViewController: 0x7fb6ddd02a90>
  • [super viewDidLoad]转换一下
- (void)viewDidLoad {
    struct objcSuper = {self, [UIViewController class]};
    objc_msgSendSuper(objcSuper, @selector(viewDidLoad));
    
    id cls = [Person class];
    void *obj = &cls;
    [(__bridge id)obj print];
}
  • 分析

1>objcSuper是局部变量,代替了之前string的位置
2>objcSuperself的地址较低,所以obj跳过cls找到的就是self

self

相关文章

网友评论

    本文标题:Runtime:super原理分析

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