zoukankan      html  css  js  c++  java
  • Runtime

    (一)[self class] 与 [super class]

      下面代码输出什么@implementation Son : Father
    - (id)init
    {
        self = [super init];
        if (self)
        {
            NSLog(@"%@", NSStringFromClass([self class]));
            NSLog(@"%@", NSStringFromClass([super class]));
        }
    return self;
    }
    @end

    self和super的区别:

      self是类的一个隐藏参数,每个方法的实现的第一个参数

      super并不是隐藏参数,它实际上只是一个“编译器标示符”,它负责告诉编译器,当调用方法时,去调用父类的方法,而不是本类中的方法。

      在调用[super class]的时候,runtime会去调用objc_msgSendSuper方法,而不是objc_msgSend。

      在objc_msgSendSuper方法中,第一个参数是一个objc_super的结构体,这个结构体里面有两个变量,一个是接收消息的receiver,一个是
    当前类的父类super_class。

      容易误认为[super class]是调用的[super_class class]。

      objc_msgSendSuper的工作原理应该是这样的:
    从objc_super结构体指向的superClass父类的方法列表开始查找selector,找到后以objc->receiver去调用这个selector。注意,最后的调用者是objc->receiver,而不是super_class!

      所以最后输出两个都一样,都是输出son。

    (二)isKindOfClass 与 isMemberOfClass

    @interface Sark : NSObject
    @end
     
    @implementation Sark
    @end
     
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
            BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
            BOOL res3 = [(id)[Sark class] isKindOfClass:[Sark class]];
            BOOL res4 = [(id)[Sark class] isMemberOfClass:[Sark class]];
     
           NSLog(@"%d %d %d %d", res1, res2, res3, res4);
        }
        return 0;
    }

    先来分析一下源码这两个函数的对象实现

    + (Class)class {
        return self;
    }
     
    - (Class)class {
        return object_getClass(self);
    }
     
    Class object_getClass(id obj)
    {
        if (obj) return obj->getIsa();
        else return Nil;
    }
     
    inline Class 
    objc_object::getIsa() 
    {
        if (isTaggedPointer()) {
            uintptr_t slot = ((uintptr_t)this >> TAG_SLOT_SHIFT) & TAG_SLOT_MASK;
            return objc_tag_classes[slot];
        }
        return ISA();
    }
     
    inline Class 
    objc_object::ISA() 
    {
        assert(!isTaggedPointer()); 
        return (Class)(isa.bits & ISA_MASK);
    }
     
    + (BOOL)isKindOfClass:(Class)cls {
        for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
            if (tcls == cls) return YES;
        }
        return NO;
    }
     
    - (BOOL)isKindOfClass:(Class)cls {
        for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
            if (tcls == cls) return YES;
        }
        return NO;
    }
     
    + (BOOL)isMemberOfClass:(Class)cls {
        return object_getClass((id)self) == cls;
    }
     
    - (BOOL)isMemberOfClass:(Class)cls {
        return [self class] == cls;
    }

    首先题目中NSObject 和 Sark分别调用了class方法。

    + (BOOL)isKindOfClass:(Class)cls方法内部,会先去获得object_getClass的类,而object_getClass的源码实现是去调用当前类的obj->getIsa(),最后在ISA()方法中获得meta class的指针。

    接着在isKindOfClass中有一个循环,先判断class是否等于meta class,不等就继续循环判断是否等于super class,不等再继续取super class,如此循环下去。

    [NSObject class]执行完之后调用isKindOfClass,第一次判断先判断NSObject 和 NSObject的meta class是否相等,NSObject的meta class与本身不等。接着第二次循环判断NSObject与meta class的superclass是否相等。Root class(meta) 的superclass 就是 Root class(class),也就是NSObject本身。所以第二次循环相等,于是第一行res1输出应该为YES。

    同理,[Sark class]执行完之后调用isKindOfClass,第一次for循环,Sark的Meta Class与[Sark class]不等,第二次for循环,Sark Meta Class的super class 指向的是 NSObject Meta Class, 和 Sark Class不相等。第三次for循环,NSObject Meta Class的super class指向的是NSObject Class,和 Sark Class 不相等。第四次循环,NSObject Class 的super class 指向 nil, 和 Sark Class不相等。第四次循环之后,退出循环,所以第三行的res3输出为NO。

    如果把这里的Sark改成它的实例对象,[sark isKindOfClass:[Sark class],那么此时就应该输出YES了。因为在isKindOfClass函数中,判断sark的meta class是自己的元类Sark,第一次for循环就能输出YES了。

    isMemberOfClass的源码实现是拿到自己的isa指针和自己比较,是否相等。

    第二行isa 指向 NSObject 的 Meta Class,所以和 NSObject Class不相等。第四行,isa指向Sark的Meta Class,和Sark Class也不等,所以第二行res2和第四行res4都输出NO。

    (三)class与内存地址

    @interface Sark : NSObject
    @property (nonatomic, copy) NSString *name;
    - (void)speak;
    @end
    @implementation Sark
    - (void)speak {
        NSLog(@"my name's %@", self.name);
    }
    @end
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        id cls = [Sark class];
        void *obj = &cls;
        [(__bridge id)obj speak];
    }
    @end

    上面的代码会怎样?

    首先需要谈谈隐藏参数self和_cmd的问题。
    当[receiver message]调用方法时,系统会在运行时偷偷地动态传入两个隐藏参数self和_cmd,之所以称它们为隐藏参数,是因为在源代码中没有声明和定义这两个参数。self在上面已经讲解明白了,接下来就来说说_cmd。_cmd表示当前调用方法,其实它就是一个方法选择器SEL。

    难点一,能不能调用speak方法?

    答案是可以的。obj被转换成了一个指向Sark Class的指针,然后使用id转换成了objc_object类型。obj现在已经是一个Sark类型的实例对象了。当然接下来可以调用speak的方法。

    难点二,如果能调用speak,会输出什么呢?

    正确的答案会输出:my name is

    内存地址每次运行都不同,但是前面一定是ViewController。why?

    我们把代码改变一下,打印更多的信息出来。

    - (void)viewDidLoad {
        [super viewDidLoad];
     
        NSLog(@"ViewController = %@ , 地址 = %p", self, &self);
     
        id cls = [Sark class];
        NSLog(@"Sark class = %@ 地址 = %p", cls, &cls);
     
        void *obj = &cls;
        NSLog(@"Void *obj = %@ 地址 = %p", obj,&obj);
     
        [(__bridge id)obj speak];
     
        Sark *sark = [[Sark alloc]init];
        NSLog(@"Sark instance = %@ 地址 = %p",sark,&sark);
     
        [sark speak];
     
    }

    我们把对象的指针地址都打印出来。输出结果:

    ViewController =  , 地址 = 0x7fff543f5aa8
    Sark class = Sark 地址 = 0x7fff543f5a88
    Void *obj =  地址 = 0x7fff543f5a80
     
    my name is
     
    Sark instance =  地址 = 0x7fff543f5a78
    my name is (null)

    按viewDidLoad执行时各个变量入栈顺序从高到底为self, _cmd, self.class, self, obj。

    第一个self和第二个_cmd是隐藏参数。第三个self.class和第四个self是[super viewDidLoad]方法执行时候的参数

    在调用self.name的时候,本质上是self指针在内存向高位地址偏移一个指针。在32位下面,一个指针是4字节=4*8bit=32bit。

    从打印结果我们可以看到,obj就是cls的地址。在obj向上偏移32bit就到了0x7fff543f5aa8,这正好是ViewController的地址。

    所以输出为my name is 。

     
  • 相关阅读:
    Redis学习之有序集合类型
    Redis学习之set类型总结
    Redis学习之List类型总结
    Redis学习之哈希类型总结
    Redis学习之字符串
    3、mysql学习之数据库定义语句
    2、mysql学习之创建用户与授权方法
    1、mysql学习之密码丢失恢复
    6、MongoDB学习之主从复制
    5、MongoDB学习之安全与认证
  • 原文地址:https://www.cnblogs.com/HMJ-29/p/5978152.html
Copyright © 2011-2022 走看看