该内容仅供自己学习记录,前前后后也积累了好多有关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,也就是上面举得例子Studentclass的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 = 元类对象的地址
本文深入探讨了Objective-C中对象的内存布局,包括NSObject对象的实际内存占用情况,以及类、实例对象和元类对象的内部结构。分析了class_getInstanceSize和malloc_size函数的内部实现,揭示了CoreFoundation要求所有对象至少为16字节的原因。
792

被折叠的 条评论
为什么被折叠?



