该内容仅供自己学习记录,前前后后也积累了好多有关Objective-C的东西,今天偶然看到一个有关MJ的视频,特此记录一下
一个NSObject对象占用多少内存字节
- 首先回答这个问题,要分为两部分
因为对象本质是个结构体,里面有isa指针指向自己所属的类(类的isa指针指向元类,元类的isa指针指向基类的meta-class对象),总归是个指针,那么,它占用的自己数是8(64bit位环境下)个字节没错了,所以通过class_getInstanceSize
这个函数打印出来的是8个字节 - 但是系统会对一个NSObject对象分配16个字节,通过
malloc_size
函数就可以打印出来
解析:
那我们接下来就看看这两个函数内部都是怎么调用的
首先我们来看 class_getInstanceSize
这个函数的源码,源码在这里下载
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
return cls->alignedInstanceSize();
}
.....
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
从这里我们就可以看出,本质上是调用alignedInstanceSize()
,该函数描述为返回Class的ivar的大小,因为一个Class只有一个isa指针,故返回8
然后我们来看malloc_size
,首先,该函数是什么时候调用的呢?当我们初始化一个实例对象的时候,例如NSObject *obj = [[NSObject alloc] init];
其实本质上并不是调用的alloc()
方法,本质上是调用的allocWithZone
,对allocWithZone方法进行跟踪后发现
id objc_alloc(Class cls)
{
return callAlloc(cls,true/*checkNil*/,ture/*allocwithZone*/)
}
id objc_allocWithZone(Class cls)
{
return callAlloc(cls,true/*checkNil*/,ture/*allocwithZone*/)
}
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
.....
id obj = class_createInstance(cls,0);
}
id class_createInstance(Class cls, size_t extraBytes)
{
return _class_createInstanceFromZone(cls, extraBytes,nil)
}
....
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, bool cxxConstruct = true, size_t *outAllocatedSize = nil)
{
....
size_t size = cls->instanceSize(extraBytes)//分配的大小
}
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
//CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
上述代码省略了非关注的问题,并且调整了顺序,当然,这些代码在源码以及编译成c/c++文件后都可以找到从,这里我们就能找到真正的原因,CoreFoundation要求所有的objects最少为16bit
对象的本质
接下来我们就要进入对OC中对象的研究,首先我们得搞清楚对象的分类
- 实例对象
instance class
- 类对象
class
- 元类对象
meta-class
为了方便理解,我们举个例子
有Studen
学生类,Person
人类,NSObject
猿人类,首先我们将问题具象化,学生当然属于人类,人类有属于猿人类,继承关系我这里不上代码,比较懒,Student -> Person -> NSObject
简单粗暴的上结构图,首先我说明这些图是来自小马哥教育课件里的,我觉得小马哥做的都清晰明了
现有:Student *xiaoming = [[Student alloc] init];
那么xiaoming
是Student
创建的一个实例对象,xiaoming
的内部结构为
假设我们Student
就已经有age这个属性了,上图就是xiaoming
这个类里面存放的东西
那么Student
里面又有什么呢?
元类
对象里面放的什么信息呢
稍微解释下:
instance图里是xiaoming
实例类的信息,以及存储的Student
属性的值
class图是Student
类信息的图,里面存放isa
指针,superclass
指针,属性信息、实例方法、协议信息,成员变量信息…
meta-class图是Student
类的元类对象,存放isa
指针,superclass
指针,类方法信息…,下面要上一张重要的图,解释下isa,superClass
这两个比较抽象的东西
这张图总结的比较好:
首先我们分析isa
指针的指向(以Student
为例,同理可分析Person
,NSObject
)
instance of Subclass
其实就是指上面举的例子xiaoming
,它的isa
指向class
,也就是上面举得例子Student
class
的isa
指向meta-class
也就是Student
的isa
是指向Student
的元类的meta-class
的isa
指向基类的meta-class
,也就是说,Student
的元类的isa
是指向NSObject
的元类的
接下来分析superclass
指针的指向
class
的superclass
指向父类的class
在这里就是Student
的superclass
指向的是Person类
meta-class
的superclass
指向父类的meta-class
- 基类的
meta-class
的superclass
指向基类的class
然后我们分析下当xiaoming
要调用自己的实例方法时的轨迹
- 先通过
xiaoming
的类的isa
指针找到Student
,然后在Student
实例方法列表中寻找实例方法,找到的话就调用,找不到的话,通过superclass
在父类Person
中去查找,找不到的话,在通过Person
中的superclass
在Person
的父类中查找
最后我们分析下Student
如何要调用类方法的轨迹
- 先通过
Student
类中的isa
指正找到Student
的meta-class
,从meta-class
中查找类方法,找不到的话,就通过superclass
在元类的父类里面去找(按照上图已经找到NSObject
的meta-class
),找不到的话,就返回到NSObject
类中去查找方法(就是上图中Root class (meta) 又有箭头指向 Root class (class))
objc_class内部结构
struct objc_class {
Class isa;
Class superclass;
cache_t cache;//方法缓存
class_data_bits_t bits; //用于缓存具体类信息
};
bits
里的内容如下
struct class_rw_t {
uint32_5 flags;
uint32_t version;
const class_ro_t *ro;
method_list_t *methods;//方法列表
property_list_t *properties;//属性列表
const protocol_list_t * protocols;//协议列表
Class fistSubclass;
Class nextSiblingClass;
char *demangledName;
}
const class_ro_t *ro;
ro中具体的结构
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
unit32_t instanceSize;//instance对象占用的内存空间
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout
const char *name; //类名
method_list_t *baseMethodList;
protocol_list_t *baseProtocols;
const ivar_list_t *ivars; //成员变量列表
const uint8_t weakIvarLayout;
property_list_t *baseProperties;
};
上述结构为最新的objc源码中的结构,xcode里面的太旧了,早已经废弃
objc_class内部结构
问题:isa指针的地址 ?= ?元类对象的地址吗?
答案是否定的
正确答案: isa & ISA_MASK = 元类对象的地址