iOS 底层探究:属性与成员变量

620 阅读5分钟

这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

1.WWDC 类结构的优化

WWDC关于runtime里面关于类的优化里面提到了clean memorydirty memory

1.1 Clean Memory

  • 加载后不会再改变的内存
  • class_ro_t是只读的,属于Clean Memory
  • 可移除,从而节省更多内存空间。如果你有需要,系统可以从磁盘中重新加载。

1.2 Dirty Memory

  • 进程运行时会发生变化的内存
  • 类结构体一旦被使用就是Dirty Memory,因为运行时会写入新的数据,例如:方法缓存
  • Dirty Memory是类数据被分为两部分的原因 Dirty MemoryClean Memory更昂贵,因为在进程运行的整个过程中,都需要被保留。通过分离出那些永远不会改变的数据,将大部分的类数据存储为Clean Memory。 类结构一经使用就会变成Dirty Memory,因为运行时会向他写入新的数据,例如创建一个新的方法缓存并从类中指向它。所以class_rw_t出现了,它可以读写类的继承关系,跟踪类的方法,属性,协议等,单只有大约10%的类会去修改它的方法,所以class_rw_ext_t出现了,90%内将不需要class_rw_ext_t,这能节省class_rw_t一半的空间。如下图

image.png

2.属性和成员变量

在iOS5之前,定义成员变量是在大括号里定义,同时用了@property声明,而且还在@implementation中使用@synthesize方法。原因是苹果将默认编译器从GCC转换为LLVM(low level virtual machine),才不再需要为属性声明实例变量了。 在没有更改之前,属性的正常写法需要成员变量+@property+@synthesize成员便利那个三个步骤。更换为LLVM之后,编译器在编译过程中发现没有新的实例变量后,就会生成一个下划线开头的实例变量。因此现在部门不必再声明一个实例变量。 现在@property声明的属性不仅仅给我们生成一个_类型的成员变量,同时也会生成setter/getter方法。

  • 属性=带下划线成员变量+setter+getter方法。
  • 实例变量:特殊的成员变量(类的实例化)。

2.1 成员变量与属性的区别

  • 成员变量:在底层只是变量的声明
  • 属性:系统会自动在底层添加_属性名变量,同时生成setter和getter方法

2.2 成员变量与实例变量的区别

  • 实例变量是一种特殊的成员变量
  • 成员变量为基本数据类型
  • 实例变量为对象类型,例如:NSObject类型
  • NSString为常量类型,属于成员变量

3.isKindOfClass

3.1方法的作用

无论调用类对象还是实例对象的isKindOfClass方法,入口函数统一为 objc_opt_isKindOfClass 找到objc_opt_isKindOfClass函数

BOOL 
objc_opt_isKindOfClass(id obj, Class otherClass) 
{ 
#if __OBJC2__ 
    if (slowpath(!obj)) return NO; 
    Class cls = obj->getIsa(); 
    if (fastpath(!cls->hasCustomCore())) { 
        for (Class tcls = cls; tcls; tcls = tcls->getSuperclass()) { 
            if (tcls == otherClass) return YES; 
         } 
         return NO; 
     } 
#endif 
    return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass); 
}
  • 获取对象isa指向的类,和传入的Class对比
  • 遍历对象isa指向类的父类,和传入的Class对比 isKindOfClass方法的作用:
  • 类对象
    • 元类 VS Class
    • 遍历:类的父类 VS Class
  • 实例对象
    • 类 VS Class
    • 遍历:类的父类 VS Class

3.2验证类对象与实例对象

示例:封装isKindOfClass函数

void isKindOfClassDemo(){ 
    BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; 
    BOOL re2 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]]; 
    NSLog(@"NSObject类对象:%hhd", re1); 
    NSLog(@"LGPerson类对象:%hhd", re2); 
    BOOL re3 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; 
    BOOL re4 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]]; 
    NSLog(@"NSObject实例对象:%hhd", re3); 
    NSLog(@"LGPerson实例对象:%hhd", re4); }

调用isKindOfClassDemo函数

isKindOfClassDemo(); 
------------------------- 
NSObject类对象:1 
LGPerson类对象:0 
NSObject实例对象:1 
LGPerson实例对象:1

3.3分析

  • res1传入的是NSObject的类,获取元类与NSObject不等,继续寻找获取元类的父类为NSObject与传入的值相等,返回true。
    • res2传入的是LGPerson的类,获取元类与LGPerson不等,继续寻找获取元类的父类为NSObject的元类,与传入的值依旧不等,继续往上NSObject元类的父类为NSObject依旧不等,再往上就是nil,最后返回false
  • res3 传入的是NSObject的实例,获取对象的类,与NSObject相等,返回true
  • res4 传入的是LGPerson的实例,获取对象的类,与LGPerson相等,返回true

4 isMemberOfClass

4.1方法的作用

找到isMemberOfClass方法

+ (BOOL)isMemberOfClass:(Class)cls { 
    return self->ISA() == cls; 
} 
- (BOOL)isMemberOfClass:(Class)cls { 
    return [self class] == cls; 
}
  • 类方法:获取类的元类,和传入的cls对比
  • 实例方法:获取对象所属的类,和传入的cls对比

4.2验证类对象与实例对象

封装isMemberOfClass方法

void isMemberOfClassDemo(){ 
    BOOL re1 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; 
    BOOL re2 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]]; 
    NSLog(@"NSObject类对象:%hhd", re1); 
    NSLog(@"LGPerson类对象:%hhd", re2); 
    BOOL re3 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]; 
    BOOL re4 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]]; 
    NSLog(@"NSObject实例对象:%hhd", re3); 
    NSLog(@"LGPerson实例对象:%hhd", re4); 
}

调用isMemberOfClassDemo函数

isMemberOfClassDemo(); 
------------------------- 
NSObject类对象:0 
LGPerson类对象:0 
NSObject实例对象:1 
LGPerson实例对象:1

4.3分析

  • res1是NSObject类调用类方法isMemberOfClassNSObject类比较,很明显,NSObject的元类NSObject本身并不相等,所以返回false
  • res2是LGPerson类调用类方法isMemberOfClassLGPerson类比较,LGPerson的元类LGPerson本身并不相等,所以返回false
  • res3是NSObject的实例调用实例方法isMemberOfClassNSObject类比较,明显她们是相同的,所以返回true
  • res4是LGPerson的实例调用实例方法isMemberOfClassLGPerson类比较,明显的他们是相同的,所以返回true