iOS底層探索alloc流程

準備工做

1.蘋果objc源碼下載算法

2.源碼編譯xcode

3.準備調試代碼:緩存

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface ABPerson : NSObject

@end

NS_ASSUME_NONNULL_END

複製代碼
#import "ABPerson.h"

@implementation ABPerson

@end

複製代碼
#import "ViewController.h"
#import "ABPerson.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    ABPerson *p1 = [ABPerson alloc];
    ABPerson *p2 = [p1 init];
    ABPerson *p3 = [p1 init];
    NSLog(@"%@ - %p - %p",p1,p1,&p1);
    NSLog(@"%@ - %p - %p",p2,p2,&p2);
    NSLog(@"%@ - %p - %p",p3,p3,&p3);
    
}

複製代碼

底層探索的三種分析方法

第一種是經過符號斷點去定位

1.添加一個alloc符號斷點,步驟以下: 圖片.pngmarkdown

圖片.png

圖片.png

圖片.png

2.在調試代碼alloc那一行添加一個斷點app

圖片.png 3.運行項目,斷點會斷點下面這個位置,可是這不是本身代碼alloc調用的,,由於在調用我準備代碼alloc以前,系統也會調用alloc,初始化不少東西。函數

圖片.png

4.取消下圖斷點以下:oop

圖片.png

5.點擊紅框圖標,執行下一步post

圖片.png 6.再將剛纔取消的斷點,使其生效,以下:優化

圖片.png

7.點擊紅框圖標,執行下一步 圖片.pngui

8.此時咱們看到libobjc.A.dylib庫下的[NSObject alloc]就是本身代碼alloc調用的,ABPerson繼承NSObject 圖片.png 9.接下來就能夠經過庫名稱、方法名在objc源碼中探索

第二種經過xcode step into和符號斷點去定位

1.刪除符號斷點,運行項目,在當前斷點位置,按住control鍵點擊紅框圖標

圖片.png 2.當出現調用objc_alloc時,下個objc_alloc的符號斷點,執行下一步 圖片.png

圖片.png

圖片.png

圖片.png 3.獲得庫的名稱了和函數名稱了

第三種經過彙編和符號斷點去定位

1.當斷點斷到本身的代碼後,查看彙編 圖片.png 2.在彙編下斷點的位置,找到了函數調用 圖片.png 3.下符號斷點objc_alloc,方法如上

alloc探索

打開編譯好的源碼,搜索alloc { 圖片.png 進入_objc_rootAlloc 圖片.png 進入callAlloc 圖片.png 若是!cls->ISA()->hasCustomAWZ()成立,執行_objc_rootAllocWithZone,再執行objc_msgSend

在ABPerson的工程中依次給alloc_objc_rootAlloccallAlloc_objc_rootAllocWithZone下符號斷點

圖片.png 圖片.png 圖片.png 發現斷點並無斷到callAlloc,這是編譯器作了優化。

編譯器優化案例

#import <Foundation/Foundation.h>

int ABSum(int a,int b){
    return  a + b;
}
int main(int argc, const char * argv[]) {
   
    int a = 10;
    int b = 20;
    int c = ABSum(a, b);
    NSLog(@"----%d",c);
    return 0;
}
複製代碼

彙編查看:

圖片.png 這是未優化的,callq調用了ABSum函數

xcode配置優化等級 圖片.png 再次運行

圖片.png 直接使用$0x1e,也就是30,並未調用ABSum函數,被優化了。

解釋了編譯器優化,接下來看_objc_rootAllocWithZone實現

圖片.png 跟進去看一下_class_createInstanceFromZone實現

圖片.png

objc源碼調試

1.源碼工程編譯target選擇KCObjcBuild

圖片.png

2.查看main.m文件,修改代碼:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGPerson *p = [LGPerson alloc] ;
        NSLog(@"%@",p);
    }
    return 0;
}
複製代碼

3.修改LGPerson代碼:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface LGPerson : NSObject

@end

NS_ASSUME_NONNULL_END

複製代碼

4.打上斷點運行工程

圖片.png 5.按照上面斷點調試方式,確保_class_createInstanceFromZone是被LGPerson調用

圖片.png

6.step into instanceSize

圖片.png 若是有緩存就返回緩存的大小。

7.step into fastInstanceSize

圖片.png 圖片.png 拿到size 再經過align16,16字節對齊 16字節對齊算法,當x=16時,

(16+15)& ~15

31&~15

31 的二進制表示:0001 1111

15 的二進制表示:0000 1111

~15的二進制表示:1111 0000

31&~15: 0001 0000

十進制表示就是16

8.沒有就經過alignedInstanceSize計算

圖片.png 經過unalignedInstanceSize拿到實例對象的實際大小,再經過word_align進行8字節對齊

圖片.png

圖片.png

8字節對齊算法: 當前LGPerson並無成員變量,只繼承了NSObjctisa,因此x=8

(8+7)& ~7

15&~7

15 的二進制表示:0000 1111

7 的二進制表示:0000 0111

~7的二進制表示:1111 1000

15&~7: 0000 1000

十進制表示就是8

計算的結果還要再作一次處理,至少要16個字節

圖片.png

對象的內存空間

1.修改LGPerson代碼:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface LGPerson : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic) int age;
@property (nonatomic) int height;
@property (nonatomic, copy) NSString *nickName;

@end

NS_ASSUME_NONNULL_END
複製代碼

2.修改main.m代碼:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        LGPerson *p = [LGPerson alloc] ;
        p.name      = @"Cooci";
        p.nickName  = @"KC";
        p.age       = 18;
        p.height    = 180;
        NSLog(@"%@",p);

    }
    return 0;
}

複製代碼

圖片.png

圖片.png

可以看到前8個字節存儲isa相關信息,age和height共用8個字節就夠了,因此LGPerson的實例對象佔用了32個字節大小。

instanceSize執行完後,須要一個obj做爲返回object_cxxConstructFromClass的參數 圖片.png 默認obj是一個髒地址,當執行了calloc,會對它進行從新賦值。

接着往下看 圖片.png

圖片.png 最後都會調用initIsa關聯對象

alloc流程圖

圖片.png

相關文章
相關標籤/搜索