iOS底層探索類的加載(二)

上篇結尾,提到了類的實現realizeClassWithoutSwift,那麼realizeClassWithoutSwift內部幹了些什麼?c++

1.調試代碼準備

下載objc818可調試源碼git

2.realizeClassWithoutSwift分析

也在該函數內也添加if判斷代碼,只查看該函數處理LGPerson的實現,方便調試github

image.png

image.png image.png

image.png

image.png

image.png

image.png 猜測方法列表應該在methodizeClass處理算法

2.1.methodizeClass分析

image.png

2.1.1.prepareMethodLists分析

image.png

fixupMethodList分析

image.pngsel添加到方法列表markdown

image.png 按照地址進行排序函數

image.png 添加未排序前打印和排序後打印oop

image.png

通過prepareMethodLists一系列處理後,查看ro方法列表post

image.png 仍是沒有數據,因此目前方法還沒加到類裏面去。測試

2.1.2 分類的引入

methodizeClass方法裏面ui

image.png

rwe是什麼?可參看這篇文章,怎麼拿到rwe

image.png

image.png

image.png

全局搜索extAllocIfNeeded,有多處使用extAllocIfNeededrwe賦值,如:

  • attachCategories
  • demangledName
  • class_setVersion
  • addMethods_finish
  • class_addProtocol
  • _class_addProperty
  • objc_duplicateClass

因此rwe必然有值。

採用反推法 全局搜索attachCategories調用的位置:

  • attachToClass
  • load_categories_nolock

全局搜索attachToClass調用的位置:

  • methodizeClass

image.pngpreviouslytrue的時候,才能調用判斷條件裏面的attachToClass方法。 previouslymethodizeClass的參數

static void methodizeClass(Class cls, Class previously) 複製代碼

全局搜索methodizeClass調用的位置:

  • realizeClassWithoutSwift

previously也是realizeClassWithoutSwift的參數 全局搜索realizeClassWithoutSwift調用的位置發現 previouslynil做爲參數,因此上圖中的方法不會調用。那麼久來到了:

image.png

全局搜索load_categories_nolock

  • loadAllCategories
  • _read_images

image.png image.png 瞭解了attachCategories被調起的流程,那麼來看看attachCategories內部怎麼實現的

2.1.3.attachCategories分析

image.png

2.1.4.attachLists 算法

image.png

3.分類和類搭配加載

分類和類搭配加載有如下四種狀況。如今對這四種狀況分別分析。 目標是在加載主類後,查看ro裏面有沒有分類數據,若是有分類數據,就說明分類加載有可能在編譯以前就完成。

3.1.分類和類都有load方法

調用順序: _read_images非懶加載類> realizeClassWithoutSwift>methodizeClass>attachToClass>load_categories_nolock>attachCategories>

3.2.分類有load方法,主類沒有

調用順序:_read_images非懶加載類 >  realizeClassWithoutSwift > methodizeClass > attachToClass

注意:沒有調用attachCategories

3.3.主類有load方法,分類沒有

調用順序:_read_images非懶加載類 >  realizeClassWithoutSwift > methodizeClass > attachToClass

注意:也沒有調用attachCategories

3.4.分類和類都沒有load方法

什麼都不會調用,對象第一次發送消息後纔會有調用。

3.5.分類加載流程跟蹤

3.5.1.只有一個分類

當前以主類和分類都有load方法爲例 測試代碼:

@interface LGPerson : NSObject
@property (nonatomic, copy) NSString *name;
- (void)personFunc;
@end

@implementation LGPerson
//更具狀況添加或刪除
+ (void)load{}
- (void)saySomething{
    NSLog(@"%s",__func__);
}
@end
複製代碼
@interface LGPerson (LGA)
@property (nonatomic, copy) NSString *cateA_name;
- (void)cateFunc;
@end

@implementation LGPerson (LGA)
//更具狀況添加或刪除
+ (void)load{}
- (void)cateFunc{
    NSLog(@"%s",__func__);
}
@end
複製代碼

realizeClassWithoutSwiftimage.png

image.png

image.png

image.png image.png mlist地址存到mlists中了

image.png image.png

3.5.2.有多個分類

補充測試代碼:

@interface LGPerson (LGB)
- (void)cateFuncB;
@end

@implementation LGPerson (LGB)
//更具狀況添加或刪除
+ (void)load{}
- (void)cateFuncB{
    NSLog(@"%s",__func__);
}
@end
複製代碼

流程跟蹤:

image.png

image.png image.png

3.5.3.分類實現load,主類沒有實現load

image.png dyld的時候已經合到一塊兒了,存在data()裏面

3.5.4.主類實現load,分類沒有實現load

image.png dyld的時候已經合到一塊兒了,存在data()裏面

3.5.5.都沒有實現

image.png image.png image.png

都沒有實現會推遲到第一次消息發送的時候進行初始化,它們的方法都已經加載到data裏面了。

4.分類的本質

1.在main.m中添加分類代碼

#import <Foundation/Foundation.h>
#import "LGPerson.h"
#import <objc/runtime.h>
@interface LGPerson (LG) <NSObject> @property (nonatomic, copy) NSString *cate_name;
@property (nonatomic, assign) int cate_age;
- (void)cate_instanceMethod1;
- (void)cate_instanceMethod2;
+ (void)cate_classMethod3;
@end
@implementation LGPerson (LG) - (void)cate_instanceMethod1{
    NSLog(@"%s",__func__);
}
- (void)cate_instanceMethod2{
    NSLog(@"%s",__func__);
}
+ (void)cate_classMethod3{
    NSLog(@"%s",__func__);
}
@end int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGPerson *p = [LGPerson alloc];
        [p cate_instanceMethod2];
        NSLog(@"Hello, World!");
    }
    return 0;
}
複製代碼

3.將main.m編譯成c++文件

clang -rewrite-objc main.m -o main.cpp
複製代碼

image.png

image.png _category_t分類結構體結構

image.png 方法列表裏面卻卻沒有getset方法,因此分類添加屬性須要本身實現getset方法關聯對象

5.懶加載類與非懶加載類的加載

是否是懶加載類:當前類是否實現load方法

  • 非懶加載類在map_images的時候加載全部的數據:map_images>map_images_nolock> _read_images>readClass>_getObjc2ClassList >realizeClassWithoutSwift>methodizeClass
  • 懶加載類數據加載推遲到第一次消息執行:lookUpImpOrForward >initializeAndMaybeRelock>realizeClassMaybeSwiftMaybeRelock>realizeClassWithoutSwift>methodizeClass

6.總結

  • 判斷是否是懶加載類:當前類是否實現load方法
  • 分類的方法和類方法同樣也是從macho裏讀取的,若是其實現load方法,就會調用attachCategories,進行一系列的計算,因此load方法耗時。
  • 沒有實現load方法,方法會在dyld的時候加載並存到data()
相關文章
相關標籤/搜索