一個NSObject對象占用多少個字節(jié)?
在
Objective-C
中,我們可以通過一些方法來獲取一個NSObject
對象占用多少字節(jié)-
代碼獲取
NSObject
實例對象的成員變量字節(jié)大小- 獲取一個NSObject實例對象的成員變量所占用的字節(jié)大小,可以用
runtime
的api,class_getInstanceSize
來獲取,得到8
/** * Returns the size of instances of a class. * * @param cls A class object. * * @return The size in bytes of instances of the class \e cls, or \c 0 if \e cls is \c Nil. */ OBJC_EXPORT size_t class_getInstanceSize(Class _Nullable cls) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
- 也可以通過
malloc
庫里的api,malloc_size
來獲取,得到16
extern size_t malloc_size(const void *ptr); /* Returns size of given ptr */
- 獲取一個NSObject實例對象的成員變量所占用的字節(jié)大小,可以用
下面是實戰(zhàn)代碼
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
// NSObject Implementation
struct NSObject_IMPL {
Class isa; // 8個字節(jié)
};
// 指針
// typedef struct objc_class *Class;
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
// 16個字節(jié)
// 獲得NSObject實例對象的成員變量所占用的大小 >> 8
NSLog(@"%zd", class_getInstanceSize([NSObject class]));
// 獲得obj指針所指向內(nèi)存的大小 >> 16
NSLog(@"%zd", malloc_size((__bridge const void *)obj));
// 什么平臺的代碼
// 不同平臺支持的代碼肯定是不一樣
// Windows粪狼、mac旨怠、iOS
// 模擬器(i386)苫耸、32bit(armv7)咬摇、64bit(arm64)
// 可以通過 命令行工具,生成C++文件
// xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
// 然后把編譯成功的cpp文件,拖入到工程中,注意 Copy items if needed 不勾選
// 然后Build Phases中刪除main-arm64.cpp編譯選項(選中文件點減號或按delete鍵)
// 這樣文件不參與編譯就不會報錯了
}
return 0;
}
- 通過生成的編譯代碼,我們知道了
NSObject
對象本質(zhì)上是C++
的結(jié)構(gòu)體
,結(jié)構(gòu)大概長這樣
// NSObject Implementation
struct NSObject_IMPL {
Class isa; // 8個字節(jié)
};
通過objc源碼實現(xiàn)一探究竟
-
現(xiàn)在蘋果的一些底層庫的核心實現(xiàn)源碼已經(jīng)開放,我們可以去官網(wǎng)下載
- 地址
https://opensource.apple.com/tarballs/objc4/
- 選擇版本號最新的下載查看
- 地址
查看源碼發(fā)現(xiàn),一旦發(fā)現(xiàn)
if size < 16 size = 16
,小于16會直接設置為16
inline 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;
return size;
}
-
CoreFoundation
框架里的硬性規(guī)定,內(nèi)存對齊,小于16
就會設置為16
用Xcode打斷點看內(nèi)存結(jié)構(gòu)
- 打上斷點
- 在
Xcode
菜單欄選中Debug
->Debug Workflow
->View Memory
- 看到的內(nèi)存結(jié)構(gòu)如下圖所示
- 也可以用常用的LLDB指令查看
- 看到的打印如下圖所示
總結(jié)
- 一個
NSObject
對象占用多少字節(jié)
回答
- 系統(tǒng)分配了
16
個字節(jié)給NSObject
對象(通過malloc_size
函數(shù)獲得) - 但是
NSObject
對象內(nèi)部只使用了8
個字節(jié)的空間(64bit環(huán)境下,可以通過class_getInstanceSize
函數(shù)來獲取),其實就是isa
擴展到有繼承結(jié)構(gòu)的對象
-
Student
繼承自NSObject
- 代碼結(jié)構(gòu)如下
struct Student_IMPL {
Class isa;
int _no;
int _age;
};
@interface Student : NSObject
{
@public
int _no;
int _age;
}
@end
@implementation Student
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Student *stu = [[Student alloc] init];
stu->_no = 4;
stu->_age = 5;
// 16
NSLog(@"%zd", class_getInstanceSize([Student class]));
// 16
NSLog(@"%zd", malloc_size((__bridge const void *)stu));
struct Student_IMPL *stuImpl = (__bridge struct Student_IMPL *)stu;
// no is 4, age is 5
NSLog(@"no is %d, age is %d", stuImpl->_no, stuImpl->_age);
}
return 0;
}
- 大概的內(nèi)存結(jié)構(gòu)圖
擴展到有多重繼承的結(jié)構(gòu)
- 如下圖繼承結(jié)構(gòu)
@interface Person: NSObject
{
int _age;
}
@end
@implementation Person
@end
@interface Student : Person
{
int _no;
}
@end
@implementation Student
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
// 16
NSLog(@"person --- %zd", class_getInstanceSize([Student class]));
// 16
NSLog(@"person --- %zd", malloc_size((__bridge const void *)person));
Student *stu = [[Student alloc] init];
// 16
NSLog(@"stu --- %zd", class_getInstanceSize([Student class]));
// 16
NSLog(@"stu --- %zd", malloc_size((__bridge const void *)stu));
}
return 0;
}
- 結(jié)構(gòu)如下
- 一個
Person
對象,一個Student
對象占用多少內(nèi)存空間? - 答案是,都是16
- 大概的內(nèi)存結(jié)構(gòu)圖
- 有內(nèi)存對齊的原因,結(jié)構(gòu)體的大小必須是最大成員大小(
16
)的倍數(shù)
Objective-C
不同數(shù)據(jù)類型占用字節(jié)大小
- 可以通過
sizeof
來獲取不同數(shù)據(jù)類型占用字節(jié)大小 -
sizeof
其實不是一個函數(shù),僅僅只是一個操作運算符罷了,編譯時就確定了的
類型 | 32位機器 | 64位機器 |
---|---|---|
BOOL | 1 | 1 |
bool | 1 | 1 |
int | 4 | 4 |
short | 2 | 2 |
long | 4 | 8 |
long long | 8 | 8 |
NSInteger | 4 | 8 |
float | 4 | 4 |
double | 8 | 8 |
CGFloat | 4 | 8 |
char | 1 | 1 |
指針地址 | 4 | 8 |