今天看了一些关于 Objc 相关的知识,也是一些基础部分的知识。使用汇编来查看 alloc
的实现过程。bash
而后分享一下 init 到底干了啥。架构
下面就一块儿看看具体流程。函数
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "CTStudent.h"
#import <objc/runtime.h>
int main(int argc, char * argv[]) {
CTStudent *st = [CTStudent alloc];
NSLog(@"size %lu", class_getInstanceSize([st class]));
return 0;
}
复制代码
为了避免加载过多的变量及方法,我直接在Main
文件里开始了。测试
说明:ui
st
的大小。那么接下来就具体看看使用汇编来看看具体流程。atom
Notespa
注意请连接真机就行调试,不要用电脑,由于电脑和收回系统架构不一样,汇编代码也是不一样的。3d
在此以前介绍几个接下来要用的汇编命令调试
跳转到指定的函数
中去方法走到这里就返回
了,不在往下面走了 具体的能够参考网上尽详的资料。alloc
这个方法的实现。因此我使用符号断点标记 alloc
, 只要通过这个方法的都会停留下来。运行,他会停留在 _objc_rootAlloc
这个函数这里. code
那么我继续使用断点符号把 _objc_rootAlloc
也就好了标记.
class_createInstance
的函数调用中去。Note:若是汇编没有走到 class_createInstance
这一步以前,就不用启用 class_createInstance
符号断点,由于系统中的不少函数初始化也会走这个方法,因此我就在这个函数以前打了一个断点,若是真的走到断点符号前面的这个断点,我就会使用 register read
来读取当前内存中的值。那么我为何会在ret
设置断点了,是由于alloc
调用完了会有返回值。
Objc_mseSend
函数。
Objc_mseSend
默认会有两个值。
Objc_mseSend(id self, SEL _cmd)
* 第一个方法的调用者
* 第二个参数是方法编号
复制代码
在内存寄存器中,x0
默认是方法调用者
, x1
是方法实现编号
. 那么结合上图能够看到上面的 x0 地址,使用 po
命令便可查看对应的值。 咱们能够看到是CTStudent
,正是咱们 alloc
的调用者. 若是肯定是咱们关注的对象,那么咱们就须要ge 5. 我把 class_createInstance
加入断点中。
class_createInstance
的调用流程中。
通过一系列的走位,最终被 return 出去了。
而后继续,被 return 到了上一次。此时控制到输出了
那么,说明此时 上的 st
已经有大小了,大小位:8 字节。那么为啥我什么都没写倒是 8 了,由于每一个类中都有一个 isa
, 是 8 字节大小。
摘自 Objc4-750 源码
inline Class
objc_object::ISA()
{
assert(!isTaggedPointer());
#if SUPPORT_INDEXED_ISA
if (isa.nonpointer) {
uintptr_t slot = isa.indexcls;
return classForIndex((unsigned)slot);
}
return (Class)isa.bits;
#else
return (Class)(isa.bits & ISA_MASK);
#endif
}
复制代码
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
复制代码
能够看出 isa
是一个 uintptr_t
类型。
那么根据汇编来看 alloc
的流程为:
alloc -> _objc_rootAlloc -> class_createInstance.
固然这里面确定省略了不少步骤,可是使用汇编来查看代码的运行步骤是颇有趣的。不妨动手试试看。固然详细的过程,我会以解析源码的过程来阐述明白。
附带今天跟踪源码画的 alloc 走位图
- (id)init {
return _objc_rootInit(self);
}
id _objc_rootInit(id obj)
{
return obj;
}
复制代码
其实就是返回了本身。
那么为啥要这么作了? 其实就是让你在 init 中作一些初始化配置等操做。
想一想,咱们平时开发都在 init 中干了啥了。
@interface Student : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSMapTable *mapTable;
- (void)save;
- (void)log;
@end
@implementation Student
- (instancetype)init
{
self = [super init];
if (self) {
self.name = @"CT_Kare";
self.mapTable = [[NSMapTable alloc] init];
}
return self;
}
- (void)save {
[self.mapTable setObject:self.name forKey:@"name"];
}
- (void)log {
NSLog(@"self.mapTable %@", self.mapTable);
}
@end
复制代码
那么,其实也是作了一些初始化操做。
总结:因此在初始化一个对象时,alloc
作了初始化准备,好比开辟对象空间,init Isa
,等操做。init 其实就是返回了初始化对象自己
,而后在 init 中作了一些初始化操做配置
而已。