运行场景,新建一个项目工程(我命名为zzz) ,创建一个新的类JSPerson,然后在view controller里面执行 代码:JSPerson * p = [JSPerson alloc]; 打上断点。
新建一个项目工程(我命名为zzz)
创建一个新的类JSPerson
在苹果的开源网站(https://opensource.apple.com/tarballs/)去download对应的源码。这边我们看的是alloc 所以找objc4-838.
在源码的main方法写上代码用来观察,alloc方法究竟做了什么。
结果我们发现-进入alloc以后的方法循序为
- alloc
- _objc_rootAlloc
- calloc
- alloc
- calloc
- _objc_rootAllocWithZone
然后返回对象
那么为什么会经历两次的call alloc 呢?
接下来我们回到开始的zzz并且在viewcontroller上写上代码
进入deBug汇编模式,
然后发现
alloc - objc_alloc - objc_messageSend-alloc-_objec-rootAlloc – _objc_rootAllocWithZone –
所以可以确定alloc 通过_objc_rootAllocWithZone方法生成对象并返回
而第一次alloc 执行了一遍fixupMessageRef
instanceSize 探索
alloc方法在objc-runtime-new.mm 文件中 _class_createInstanceFromZone方法创建并分配内存空间。
其中InstanceSize为计算创建的这个实例对象分配多大的内存空间
static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
int construct_flags = OBJECT_CONSTRUCT_NONE,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
ASSERT(cls->isRealized());
// Read class's info bits all at once for performance
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_t size;
size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
if (slowpath(!obj)) {
if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
return _objc_callBadAllocHandler(cls);
}
return nil;
}
if (!zone && fast) {
obj->initInstanceIsa(cls, hasCxxDtor);
} else {
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
if (fastpath(!hasCxxCtor)) {
return obj;
}
construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
return object_cxxConstructFromClass(obj, cls, construct_flags);
}
inline size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
/*其中extraBytes为额外字节,alignedInstanceSize为字节对齐的方法--计算对象需要内存大小8字节对齐
caloc 系统分配的内存---16字节对齐
if (size < 16) size = 16;说明类最小的字节为16字节*/
结构体内存对齐的规则(苹果结构体内存的计算方式)
1:数据成员对齐规则:结构(struct)的第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储)。
2:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储)。
3:收尾工作:结构体的总大小,也就是sizeof的结果必须是其内部最大成员的整数倍,不足的要补齐。
struct person1{
int age ; //4 [0-3]
short leg; //2 [4-5]
char name; //1 [6]
long hight; // 8 [7,8-15]
short hand; //2 [16-17]
long finger;//8 [18,19,20,21,22,23,24-31] ==> 32
}person1;
struct person2{
int age ; // 4 [0-3]
long hight; // 8 [4,5,6,7,8-15]
short hand; // 2 [16-17];
long finger; // 8 [24-31] ==>32
struct person1 teammate; //32 ====64
}person2;
为什么苹果计算内存使用8字节而,系统分配内存使用16字节计算呢,16字节分配这就是苹果的空间换时间的最优算法,分配空间太小那么计算量增加,如果空间分配多了又会造成分配内存过多即资源过度浪费。16字节就是资源分配最优的解。
对象本质就是objc——object – isa(类的指针) + 成员变量的值
在终端输入; clang -rewrite-objc main.m 编译一下.m文件会输出一个main.cpp 文件
然后我们打开可以看到所有类都是 objc-object 的结构体,对象= isa指针 加上成员变量的值