一個NSObject對象佔用多少內存?
🌰NSObject *object = [[NSObject alloc] init];
//獲取類對象所佔有內存大小
NSLog(@"%zu", class_getInstanceSize([object class]));
//獲取NSObject實例對象所佔用內存大小
NSLog(@"%zu", malloc_size((__bridge void *)object)); //16class_getInstanceSize是獲取類對象在內存中的大小是8個字節,而malloc_size是獲取實例對象在內存中的大小是16個字節,那究竟一個NSObject在內存中是佔用8個字節還是16個字節呢,我們來繼續深入了解。
類對象大小struct NSObject_IMPL {
Class isa;
};
size_t class_getInstanceSize(Class cls)
{
return cls->alignedInstanceSize();
}
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
將代碼轉化為cpp源碼,可以看到類對象底層實現其實就是一個簡單的結構體,包含一個isa的類對象指針,在64位系統下,一個指針佔用內存就是8個字節!class_getInstanceSize源碼底層是獲取類對象中變量所佔用的內存大小,因為類對象結構體中只有一個isa指針變量,所以一個類對象佔用內存大小是8個字節。所以類的本質是一個佔用內存八個字節的結構體!實例對象大小inline size_t instanceSize(size_t extraBytes) const {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}當我們使用alloc方法生成一個對象底層會調用到instanceSize方法,如果我們的對象中變量所佔用的內存大小小於16個字節的話,系統默認會幫我們將對象內存大小擴充到16個字節,而實際中對象所使用到的內存只有8個字節而已!
🌰
@interface JZPerson : NSObject
{
@public
int age;
int num;
}
@end
@implementation JZPerson
@end
@interface JZBoy : JZPerson
{
@public
int sex;
int sex2;
}
@end
@implementation JZBoy
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
JZBoy *boy = [[JZBoy alloc] init];
NSLog(@"%zu", class_getInstanceSize([JZBoy class]));
NSLog(@"%zu", malloc_size((__bridge void *)boy));
}
return 0;
}
第一行輸出結果是24,這裡涉及到內存對齊問題,結構體的大小必須是最大成員內存大小的倍數,本來這裡的變量佔用內存大小只有20個字節,但是系統會幫我們分配24個字節,即8的整數倍。第二行輸出的結果是32,這裡到底為什麼是32,我們要繼續分析alloc方法的底層實現代碼!_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone)
{
void *bytes;
size_t size;
//獲取對象中變量實際佔用內存大小,extraBytes一般為0
size = cls->alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
//這個方法蘋果幫我們的內存大小做了處理
bytes = calloc(1, size);
return objc_constructInstance(cls, bytes);
}
calloc(size_t num_items, size_t size) {
#define NANO_MAX_SIZE 256 /* Buckets sized {16, 32, 48, ..., 256} */
}
我們下載了malloc源碼,可以看到蘋果底層是以桶的概念來分配內存的,而且分配對象內存大小是以16位元組的整數倍來進行分配的!小結
類的本質其實是結構體來實現的,佔用的內存大小為一個isa的類指針大小!實例對象佔用內存大小其實是按照16的整數倍來進行內存大小的分配,以空間換時間,提高系統的運行效率!對象的isa指針指向哪裡?
🌰# define ISA_MASK 0x00007ffffffffff8ULL
@interface Person : NSObject
{
@public
int _age;
}
@property (nonatomic, assign) int height;
@end
@implementation Person
@end
//為了獲取類對象的isa指針地址
struct NSObject_Class {
Class isa;
Class superclass;
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
//instance 實例對象
Person *personObject = [[Person alloc] init];
//class 類對象
Class personClass = [personObject class];
//meta-class 元類對象
Class personMetaClass = object_getClass(personClass);
struct NSObject_Class *objectClass = (__bridge struct NSObject_Class *)personClass;
struct NSObject_Class *metaClass = (__bridge struct NSObject_Class *)personMetaClass;
}
return 0;
}OC底層實現源碼其實就是將對象的isa指針&ISA_MASK就得到了父類的地址。
實例對象personObject的isa&ISA_MASK = personClass類對象的地址;類對象personClass的isa&ISA_MASK = personMetaClass元類對象的地址;這個就是驗證的結果:
(lldb) p/x personObject->isa
(Class) $0 = 0x001d8001000011d9 Person
(lldb) p/x personClass
(Class) $1 = 0x00000001000011d8 Person
(lldb) p/x 0x001d8001000011d9 & 0x00007ffffffffff8ULL
(unsigned long long) $2 = 0x00000001000011d8
(lldb) p/x objectClass->isa
(Class) $3 = 0x00000001000011b0
(lldb) p/x personMetaClass
(Class) $4 = 0x00000001000011b0
(lldb) p/x 0x00000001000011b0 & 0x00007ffffffffff8ULL
(unsigned long long) $5 = 0x00000001000011b0小結
class對象的isa指向meta-class對象;meta-class對象的isa指向基類的meta-class對象;OC的類信息存放在哪裡?
🌰@interface Person : NSObject<NSCoding>
{
@public
int _age;
}
@property (nonatomic, assign) int height;
@end
@implementation Person
- (void)name {
NSLog(@"name");
}
+ (void)eat {
NSLog(@"eat");
}
@end通過底層源碼來窺探實例對象變量,方法,類方法等信息的存儲情況:
小結
對象方法、屬性、成員變量、協議信息,存放在class對象中;isa、superclass總結meta-class的isa指向基類的meta-classclass的superclass指向父類的class,如果沒有父類,superclass指針為nilmeta-class的superclass指向父類的meta-class,基類的meta-class的superclass指向基類的類對象classinstance調用對象方法的軌跡isa找到class,方法不存在,就通過superclass找父類class調用類方法的軌跡isa找meta-class,方法不存在,就通過superclass找父類