美文网首页
03.runtime答疑

03.runtime答疑

作者: 白开了杯水 | 来源:发表于2021-09-13 17:56 被阅读0次

    之前章节
    01Class的结构
    02.消息机制

    1.以下代码输出了什么?
    @interface Person : NSObject
    @end
    
    @interface Student : Person
    @end
    
    @implementation Student
    - (instancetype)init
    {
        self = [super init];
        if (self) {
            NSLog(@"[self class] = %@", [self class]);
            NSLog(@"[self superclass] = %@", [self superclass]);
            NSLog(@"[super class] = %@", [super class]);
            NSLog(@"[self superclass] = %@", [super superclass]);
        }
        return self;
    }
    @end
    

    首先self不用说,1和2输出分别是Student和Person,至于3和4,主要考的其实是super的底层,super的底层源码我们使用clang编译为c++代码后如下:

    /// Specifies the superclass of an instance. 
    struct objc_super {
        /// Specifies an instance of a class.
        __unsafe_unretained _Nonnull id receiver;
        __unsafe_unretained _Nonnull Class super_class;
        /* super_class is the first class to search */
    };
    
    • receiver:消息接收者
    • super_class:看注释很明白,就是找方法从父类方法开始找

    那么我们的代码[super class]其实转过来就是大概如下

    objc_msgSendSuper({self, Person}, @selector(class));
    

    由于receiver是消息接收者,self对应着receiver,所以最终还是会对self发送class的消息,这时其实相当于[self class]和[self superclass],结果就是Student和Person,如下图

    输出结果
    2.以下代码输出了什么?
    BOOL res1 = [[Student new] isKindOfClass:[Person class]];
    BOOL res2 = [[Student new] isMemberOfClass:[Person class]];
    BOOL res3 = [[Person class] isMemberOfClass:[NSObject class]];
    BOOL res4 = [[Person new] isMemberOfClass:[Person class]];
    NSLog(@"%d %d %d %d ", res1, res2, res3, res4);
    

    其实这个主要考察的是isKindOfClass和isMemberOfClass的区别了,这俩个方法分别是在NSObject实现的,而且是开源的,我们可以先看看它们的源码实现

    + (Class)superclass {
        return self->getSuperclass();
    }
    
    - (Class)superclass {
        return [self class]->getSuperclass();
    }
    
    + (BOOL)isMemberOfClass:(Class)cls {
        return self->ISA() == cls;
    }
    
    - (BOOL)isMemberOfClass:(Class)cls {
        return [self class] == cls;
    }
    
    + (BOOL)isKindOfClass:(Class)cls {
        for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
            if (tcls == cls) return YES;
        }
        return NO;
    }
    
    - (BOOL)isKindOfClass:(Class)cls {
        for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
            if (tcls == cls) return YES;
        }
        return NO;
    }
    
    • isKindOfClass:比较多次,它首先和cls参数相比,然后继续跟父类相比,也就是会循环到父类去,跟继承有关系
    • isMemberOfClass:比较一次,只跟传进来的cls对比
      输出结果自然是:1 0 0 1
    3.以下代码是否能运行成功,可以的话输出什么
    @interface Person : NSObject
    @property (nonatomic, copy) NSString *name;
    - (void)print;
    @end
    @implementation Person
    - (void)print
    {
        NSLog(@"name = %@", self.name);
    }
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        id cls = [Person class];
        void *obj = &cls;
        [(__bridge id)obj print];
    }
    @end
    

    这道题太蛋疼了,我也不太懂,要画图才能理清思绪

    id cls = [Person class];
    

    那么画出来的图如下:

    cls内存分布结构模拟图

    接下来

    void *obj = &cls;
    
    obj内存分布结构模拟图

    从图中可以看出,obj指向的是cls的地址,而cls的地址指向的是PersonClass的地址,而PersonClass的地址就是isa的地址(首地址)
    最后print方法如下图:

    print方法调用内存分布结构模拟图

    简单来说,代码里有调用[super viewDidLoad],这里系统会创建一个变量self传入进去给到receiver,所以会分配一个栈空间内存,栈空间内存机制是从高位向地位开始分配器,所以self内存地址会大一些,如0x009这样,然后接下来到cls,这时会分配0x001这样子,当我调用name的属性的时候,isa是0x001,那么加多8个字节,就是0x009,也就是说name的地址值就是0x009,那0x009不就是self吗,所以结果会输入控制器值:name = ViewController: 0x....这样

    相关文章

      网友评论

          本文标题:03.runtime答疑

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