isa继续探索
在对象探索-初探isa文章中,简单探索了isa的构成,这篇文章主要将isa之间的关系及类的内存结构。
isa链
isa指向谁
实例对象和类都有isa,实例对象的isa指向类,类的isa指向元类,这个是我们都知道的。接下来我们来证明。
方式1:源码证明
//objc.h
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
//objc-runtime-new.h
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
/**省略**/
}
//objc-private.h
struct objc_object {
private:
isa_t isa;
/**省略**/
}
从源码中,我们可以看到都以isa,也得到了object是Class的实例的答案。但是,没有看到Class的isa是指向元类的答案。这个是是由系统控制的,确实存在的,在第二种方式中可以得到证明。
注意:在看源码是,你会发现有old和new两种文件,一定要看new。因为现在都是新objc。
方式2:lldb调试证明
定义一个Person类,是继承自NSObject
@interface Person : NSObject
{
NSString *hobby;
}
@property(nonatomic, copy)NSString *name;
@end
#import "Person.h"
@implementation Person
@end
上断点,运行查看Person对象的内存情况
实例对象第一个位置是isa,我们看看他是不是:
完美,验证了他所属的类是Person
接下来验证Person类的isa是谁?
将Person输出:
看Person类的内存情况
取出Person类第一个位置,查看他是谁?
此时,你看到输出的是Person,这是第二次输出Person。第一次是对象的isa是Person,第二次是Person类的isa是Person。你也许会有疑问
,内存中会存在两个类对象,这是错误的,接下来会验证这个问题。那么,Person类的isa是Person,实际上它是Person的元类Person,由系统生成。
此时,你心里会想继续深入,元类Person的isa是谁?答案是NSObject,不慌,马上验证:
元类NSObject的isa是谁?
元类NSObject的isa是他自己,当然,你可能说它是NSObject类。但是可以直接查看NSObject.class,你会发现他们不一样:
由以上可以得到一条isa链:对象->类->元类->根元类->它自己
一张图可以表明他们的关系
类对象在内存中存在几份?
类对象在内存中只有一个。对象只是类的一个代表,如果类有多份,同一个类有多个对象,就有多个类对象。那么,造成相同的类重复的占用内存,造成资源浪费,
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
Person *objc = [Person alloc];
Person *objc1 = [Person alloc];
NSLog(@"class:%@,%@,%@,%@",objc.class,objc1.class,Person.class,object_getClass([Person alloc]));
}
return 0;
}
输出结构及验证,证明了类对象在内存中只有一份:
具有继承关系的类,他们的对象有关系吗?
他们的对象没有关系。继承是类之间的关系,对象之间不存在继承,他们只是类的具体表现形式
object、class的isa以及与实例对象的关系
从objc_object、objc_class的源码中,可以得到如下关系:
objc_object、objc_class、NSObject有何关系?
NSObject低层实现是objc_class,NSObject实例的低层是现是objc_object,他们是模版一样,类与对象按照他们来生成。objc_class是继承自objc_object,自动拥有isa。
探索类的内存
object、class的isa以及与实例对象的关系关系图中,可以得知objc_class的第一个是isa,第二个是superclass,第三个是cache,第四个是data。那么接下来验证一下data。
1)第一第二个是isa及superclass,偏移16字节就是cache
2)要找到data,必须知道cache占用多大内存
3)cache地址加上自己占用内存大小就是data的地址:0x1000021d8+16(0x100002110)=0x1000021e8
4)取出class_rw_t
5)这个要注意一下,在781之前,属性、方法等是一个变量,在781版本变为了一个函数。接下来取属性
总结
ro:编译时生成
rw:运行时生成,此时,会将ro移到rw
rwe:如果有动态修改,比如,添加方法、属性、类别(非懒加载),会生成rwe,同时,将rw中的ro拷贝一份到rwe。
为什么要设计元类?
复用消息机制。因为发送消息底层是objc_msgSend(接受者,消息编号,参数),如果没有元类,类方法和实例方法可能会同名,那么,objc_msgSend就要区分是什么类型的方法