(一)OC語言的本質
- 其實我們編寫的OC代碼,底層實現都是C/C++代碼
-
Objective-C的面向對象都是基于C\C++的數據結構實現的
語言轉化流程
那么,是基于什么數據結構實現的呢?結構體
(1)將OC代碼轉換為C++代碼
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 輸出的CPP文件
-
xcrun
Xcode run簡寫 -
-sdk iphoneos
指定SDK 運行的OS平臺 -
clang
編譯器的一種 -
-arch arm64
聲明架構代碼(i386 、armv7 、arm64) -
-rewrite-objc
重寫objc代碼 -
-o
輸出 - 如果需要鏈接其他框架,使用-framework參數。如-framework UIKit
示例:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
(2)NSObject的本質
我們在.cpp文件中,找到這樣一段代碼,發現NSObject底層實現
struct NSObject_IMPL {//NSObject implementation
Class isa;//很重要,后面會講到
};
另外我們在OC文件中,找到NSObject頭文件的定義
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
通過這個我們可以側面證明,NSObject的底層實現是通過C/C++的結構體的數據結構實現的
isa指針如果不好理解的話,isa指針可以類比數組的首元素,數組的首元素的地址也就是數組的首地址
(2)NSObject的占用的內存大小
我們繼續查看isa,發現是這么定義的
typedef struct objc_class *Class;
這是一個指向結構體的指針
如下面的代碼,我們通過runtime和malloc的方法得到NSObject所占用內存大小的信息,為什么會有8和16的區別呢?
#import <objc/runtime.h>
#import <malloc/malloc.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSObject *obj = [[NSObject alloc] init];
//獲得NSObject類的實例對象中成員變量所占內存的大小(內存對齊后)
NSLog(@"%zd",class_getInstanceSize([obj class]));//8
//獲得obj指針所指向內存的大小
NSLog(@"%zu",malloc_size((__bridge const void *)obj));//16
}
return 0;
}
查看runtime源碼找到class_getInstanceSize
方法
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
return cls->alignedInstanceSize();
}
//再深入查看,發現這一條解釋 返回的是成員變量占用的內存大小
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
而在NSObject調用alloc
方法實際上allocWithZone
方法,在runtime中最后調用的是_objc_rootAllocWithZone
方法,我們深入查看,最后找到instanceSize
方法,是指定對象內存大小的方法
NEVER_INLINE
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);
}
size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
//很重要!!!
if (size < 16) size = 16;//如果不足16個字節,強制分配16個字節
return size;
}
所以也就能解釋,系統給NSobject對象分配16個字節,實際使用到的只有8個字節,分別通過class_getInstanceSize
函數和malloc_size
函數可以知道
(3)容易混淆的兩個函數
class_getInstanceSize
-
malloc_size
上面用到了兩個函數,很容易混淆他們之間的區別
創建一個實例對象,(內存對齊后)至少需要多少內存?
#import <objc/runtime.h>
class_getInstanceSize([NSObject class]);
創建一個實例對象,實際上分配了多少內存?
#import <malloc/malloc.h>
malloc_size((__bridge const void *)obj);
(3)窺探NSObject的內存
(4)常用的LLDB指令
具體窺探內存方法以及LLDB指令的使用在 Swift-七、枚舉類型 可選項也有講述