iOS-對象的本質-一個OC對象佔多少內存

我們平常敲的Objective-C代碼,底層實現其實是C/C++代碼.那麼一個OC對象佔用多少內存.

iOS開發中Objective-C和swift用的是Clang/LLVM來編譯的.
使用的clang編譯器編譯成cpp,xcodebuild負責將OC/Swift工程編譯成xxx.app,xcrun負責給xxx.app簽名並打包成xxx.ipa
 

#import <Foundation/Foundation.h>
int main(int argc, char * argv[]) {
    @autoreleasepool {
        NSObject *obj = [[NSObject alloc] init];
        NSLog(@"hello world!");     
    }
    return 0;
}

通過clang編譯器編譯成cpp,執行

clang -rewrite-objc main.m -o main.cpp

我們也可以指定iphoneos/arm64,執行

 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main64.cpp

生成main64.cpp,其中main函數的執行代碼轉換成C/C++的代碼:

#pragma clang assume_nonnull end

int main(int argc, char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
        
        /**
         這裏我們可以看到objc_msgSend(id,SEL)

         @param NSObject 類型是NSObject
         @return 返回類型是NSObject
         */
        NSObject *object = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_9d_y4q9htmj3q17ljhpchdzt5vc0000gp_T_main_2c6acf_mi_0);
    }
    return 0;
}

在這裏我們看到熟悉的面孔__autoreleasepool和objc_msgSend,一個是自動釋放池,一個是消息傳遞
自動釋放池內部用的是雙向鏈表,通過標記衛士來刪除
 

/**
 這裏我們看到push入棧生成atautoreleasepoolobj
           pop出棧生成atautoreleasepoolobj
 */
struct __AtAutoreleasePool {
  __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
  ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  void * atautoreleasepoolobj;
};

接着我們看到

//OC轉成C++的代碼
struct NSObject_IMPL {
	Class isa;
};
typedef struct objc_class *Class;
struct objc_object {
    Class _Nonnull isa __attribute__((deprecated));
};

// runtime.h
struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

可以看到我們回到了NSObject就是一個對象,就是一個結構體指針
執行過程:objc_getClass-->(objc_class *)objc_getClass-->objc_class結構體
也就是class就是一個指針,指針在32位是4字節,64位是8字節

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
int main(int argc, char * argv[]) {
    @autoreleasepool {
        NSObject *obj = [[NSObject alloc] init];
        NSLog(@"hello world!");
        //獲得NSobject對象實例成員變量佔用的大小 ->8
        size_t size = class_getInstanceSize(obj.class);
        //獲取NSObjet指針的指向的內存大小 ->16
        size_t size2 = malloc_size((__bridge const void *)(obj));
        NSLog(@"size;%zu size2:%zu",size,size2);
        
    }
    return 0;
}

執行結果:size;8 size2:16

也就是說obj就是一個指向class的指針,如果機器是64爲, obj指針是8字節,指針指向的內存大小爲16字節.
Person添加兩個成員變量
 

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
@interface Person : NSObject
{
    @public
    int _age;
    int _no;
}
@end
int main(int argc, char * argv[]) {
    @autoreleasepool {
//        NSObject *obj = [[NSObject alloc] init];
//        NSLog(@"hello world!");
        Person *obj = [[Person alloc] init];
        obj->_age = 15;
        obj->_no  = 14;
       //獲取NSObject對象實例成員變量佔用大小  8
        size_t size = class_getInstanceSize(obj.class);
       //獲取NSObject指針指向的內存大小是   16
        size_t size2 = malloc_size((__bridge const void *)(obj));
        NSLog(@"size;%zu size2:%zu",size,size2);
    
    }
    return 0;
}
//轉爲C++
struct Person_IMPL {
	struct NSObject_IMPL NSObject_IVARS;//8bytes
	int _age;  //4bytes
	int _no;   //4bytes
};
輸出結果:size;16 size2:16

實例對象其實是結構體,佔用內存是16倍數,實例對象大小不受方法影響,受實例成員變量多少影響
參考:https://juejin.im/post/5d15887ee51d45108126d28d

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章