三种调试方法
- 1.符号断点
- 1.打上断点
- 2.按住control键 点击step into
- 3.即可查看底层调用方法
- 4.由于无法继续stepinto查看objc_alloc 需要手动添加断点调试
- step into 即可找到底层源码 libobjc.A.dylib`objc_alloc:
- 1.打上断点
- 2.查看汇编调试
- 打开汇编调试,即可在断点处进入汇编查看
- 2.也可在汇编方法前打上断点,再step into
- 打开汇编调试,即可在断点处进入汇编查看
- 3.当无法确定调用了什么方法时但是至少知道alloc
- 1.写上方法名打上断点
- 1.写上方法名打上断点
alloc的源码分析
-
源码怎么编译成功的什么的我就不太懂了给个链接 juejin.cn/post/697132… 大家可以看看这位怎么做的。
-
1.
LGPerson *p = [LGPerson alloc] ; -
2.
+ (id)alloc {return _objc_rootAlloc(self);} -
3
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
- 4.进入这里思考是先调用 _objc_rootAllocWithZone 还是 调用 objc_msgSend
- 5.将_objc_rootAlloc、callAlloc、_objc_rootAllocWithZone,下好符号断点,并打开汇编调试。查看得知先调用的时_objc_rootAllocWithZone再调用objc_msgSend
- 6.再往下走就可以查看_objc_rootAllocWithZone
- 7.源码查看 LGPerson类
- 8.分配一块脏地址
- 9.进入 obj = (id)calloc(1, size);
- 10.进入下一步后 发现地址发生改变
- 11.地址与类进行绑定
- 打印发现绑定成功
- hasCxxDtor 绑定方法和函数
- 打印发现绑定成功
- 12.返回出去了这个对象
开辟内存流程
-
对象的内存大小取决于成员变量属性。
-
计算对象内存大小
-
缓存
-
May be unaligned depending on class's ivars.
-
类的IVAR可能未对齐。
- Class's ivar size rounded up to a pointer-size boundary.
- 类的ivar大小向上舍入为指针大小边界。
- 说明这里对类的成员变量做了 字节对齐
- 得到的结果为 8 的整数倍 向上取整
- 假如 x = 8
- X+7 & ~7 -> 8 + 7 & ~ 7
- 8 : 0000 1000
- 7 : 0000 0111
- ~7 : 1111 1000
- & 运算 相同为1 不同为0
- 15 0000 1111
- & 1111 1000
----------------
0000 1000
- 得到结果为 8
- 假如 X = 9
- 9 : 0000 1001
- 7 : 0000 0111
- ~7 : 1111 1000
- & 运算 相同为1 不同为0
- 16 0001 0000
- & 1111 1000
----------------
0001 0000
- 得到结果为 16
我认为可以理解为 X / 8 = 商 --余数
- 当有余数时 为 (商+1)*8
- 没有余数时 为 商 * 8
位运算 >> 3 右移 3位 即 / 1000 如 15 : 0000 1111 >> 3 = 0000 0001 111
<< 3 左移 3位 即 * 1000 如 15 : 0000 1111 << 3 = 0111 1000
所以 15 右移 3位 后 再 左移 3 位 得到了 0000 1000 为 8
-
对象的内存大小由 isa (8字节) + 成员变量的大小
-
同时这里规定了如果内存大小小于16则设置为16
-
字节对齐 8 字节 64位 为 8字节
-
内存对齐 16 字节
-
通过isa 获取类 地址
-
isa & isa_mask
整理流程
- 1._objc_rootAlloc 传入 类
+ (id)alloc {
return _objc_rootAlloc(self);
}
- 2.callAlloc 这里有个同时传入 false 和 true
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
- 3._objc_rootAllocWithZone 出入了 cls
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__ //是否有可用的编译器优化
//slowpath 大概率为假
//fastpath 大概率为真
//提升编译效率。
if (slowpath(checkNil && !cls)) return nil;
//判断一个类是否有自定义的 +allocWithZone 实现,没有则走到if里面的实现
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
return _objc_rootAllocWithZone(cls, nil);
}
#endif
// No shortcuts available. 没有可用的快捷方式
if (allocWithZone) {
return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
}
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}
- 4._class_createInstanceFromZone
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
// allocWithZone under __OBJC2__ ignores the zone parameter
return _class_createInstanceFromZone(cls, 0, nil,
OBJECT_CONSTRUCT_CALL_BADALLOC);
}
- 5.真正核心的地方
_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) {
//将这块可使用的地址isa与cls类相关联
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);
}
//slowpath 大概率为假
//fastpath 大概率为真
//此处去掉slowpath和fastpath对代码逻辑没有丝毫影响,应该只是告诉编译器对代码优化,提升编译效率。
计算内存大小过程
-
计算需要开辟多少的内存空间
-
流程示意图