このブログでは、主にOCオブジェクトallocのプロセスについて説明します。これは、主に2つのステップに分かれています。1:allocメソッドの実行プロセスを調べる方法2:alloc関数の実行プロセスを分析する
以下は、OCオブジェクト割り当てのフローチャートです。
1:allocメソッドの実行フローを調べる方法
上の図に示すように、これはOCオブジェクトの割り当てプロセスで呼び出されるいくつかのメソッドです。
- シンボリックブレークポイントは、xcodeと組み合わせてxcodeシンボリックブレークポイントを介して直接設定できます(コマンドを押したまま、ステップインをクリックして入力します)
オブジェクトのインスタンス化を表示するコード行にブレークポイントを設定します(プログラムの実行時に多数のオブジェクトが作成されるため、干渉を取り除くために、最初にシンボリックブレークポイントを無効にしてから開くことができます)ブレークポイントに到達すると。)この時点で、objc_allocがallocによって内部的に呼び出されていることがわかります。次に、シンボリックブレークポイントを直接設定できます。objc_alloc関数をインターセプトして、内部で呼び出されるメソッドなどを確認します。
2.逆アセンブリのデバッグを通じてコールチェーンを表示します
[常に逆アセンブリを表示]を選択し、 objc_alloc結合シンボルブレークポイントをすばやく見つけ、コマンドを押したまま、[ステップイン]をクリックして入力し、ステップインします。
3.ソースコードを直接分析します。objc_allocは上記のシンボリックブレークポイントと逆アセンブルデバッグを通じてlibobjc.A.dylibの関数であることがわかります。アップルのオープンソースにアクセスして、コンパイルおよびシングルステップデバッグ用のソースコードをダウンロードします。
2:alloc関数の実行フローを分析します
当实例化一个NSObject类的实例,反汇编+源码调试,会发现并没有执行NSObject的类方法alloc,而是直接执行的objc_alloc
1. alloc
+ (id)alloc {
return _objc_rootAlloc(self);
}
复制代码
2. objc_alloc
id objc_alloc(Class cls)
{
return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
复制代码
callAlloc
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
if (slowpath(checkNil && !cls)) return nil;
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));
}
复制代码
在callAlloc方法中,会发现会进入if (fastpath(!cls->ISA()->hasCustomAWZ()))
inline Class
objc_object::ISA(bool authenticated)
{
ASSERT(!isTaggedPointer());
return isa.getDecodedClass(authenticated);
}
// class or superclass has default alloc/allocWithZone: implementation
// Note this is is stored in the metaclass.
#define FAST_CACHE_HAS_DEFAULT_AWZ (1<<14)
bool hasCustomAWZ() const {
return !cache.getBit(FAST_CACHE_HAS_DEFAULT_AWZ);
}
复制代码
由此可以看出,当调用callAlloc,先通过当前cls的ISA 返回一个Class对象,然后判断当前cache中是否有缓存,如果有,执行_objc_rootAllocWithZone,如果没有,执行objc_msgSend, 继续运行程序, 会发现NSObject在cache中已经有缓存,直接执行了_objc_rootAllocWithZone
_objc_rootAllocWithZone
id
_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);
}
复制代码
_class_createInstanceFromZone
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);
}
复制代码
_objc_rootAllocWithZone -> _class_createInstanceFromZone 创建一个类的实例,分三步:
1.cls->instanceSize 计算对象所需要的的内存大小
2.cls->calloc 申请开辟内存
3.cls->initInstanceIsa 通过isa,将对象于类关联
然后直接返回obj
添加一下自定义类型调用alloc
会发现p1实例在alloc执行流程中,cache中没有缓存,使用runtime objc_msgSend函数调用了alloc方法,然后再调用 _objc_rootAlloc -> callAlloc -> _objc_rootAllocWithZone -> _class_createInstanceFromZone -> instanceSize -> calloc -> initInstanceIsa 然后跳过p1, 会发现p2的alloc执行流程与上面的NSObject一样,就不会通过runtime的 objc_msgSend调用alloc方法,因为此时缓存中已经有LGPerson的缓存了,就直接 _objc_rootAllocWithZone执行后面的流程
总结
カスタムクラスallocのプロセス:objc_alloc-> callAllocを呼び出すと、callAllocにキャッシュとキャッシュなしの2つのケースがあります。キャッシュがある場合は、_objc_rootAllocWithZone- > _class_createInstanceFromZone- > instanceSize- > calloc- > initInstanceIsa no cache is、 callAlloc allocメソッドはobjc_msgSendを介して内部的に呼び出され、 _objc_rootAlloc- > callAllocはallocメソッド内で実行されます。次の手順は上記と同じです。