@implementation MNSubclass
- (void)compareSelfWithSuperclass{
NSLog(@"self class = %@",[self class]);
NSLog(@"super class = %@",[super class]);
}
@end
複製代碼
咱們平時編寫的Objetcive-C,底層實現都是C/C++實現的 html
@interface MNPerson : NSObject
{
int _age;
double _height;
NSString *name;
}
複製代碼
以MNPerson
爲例,裏面的成員變量有不一樣類型是,好比int
、double
、NSString
類型,假如在C/C++ 中用數組
存儲,顯然是不太合理的ios
結構體
的數據格式,表示oc對象。// 轉成c/c++ 代碼後,MNPerson 的結構以下
struct NSObject_IMPL {
Class isa;
};
struct MNPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
double _height;
NSString *name;
};
複製代碼
使用指令
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc oc源文件 -o 輸出的c++文件
將oc
代碼轉成c++
代碼以後,發現內部確實是結構體c++
一個NSObject 對象,佔用多少內存git
思路:github
NSObject
的本質是結構體,經過NSObject.m
能夠發現,NSObject
只有一個 isa
成員,isa
的本質是 class
, struct objc_object *
類型,因此應該佔據 8 字節NSLog(@"%zu",class_getInstanceSize([NSObject class]));
輸出 - size = 8注意!實際上,面試
{
//得到 - NSObject 一個實例對象的成員變量所佔用的大小 >> 8
NSLog(@"%zu",class_getInstanceSize([NSObject class]));
NSObject *obj = [[NSObject alloc]init];
// 獲取 obj 指針,指向的內存大小 >> 16
NSLog(@"%zu",malloc_size((__bridge const void *)obj));
}
複製代碼
//基於 `objc-class.m` 文件 750 版本
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()
{
return word_align(unalignedInstanceSize());
}
複製代碼
alloc init
, 查找alloc底層實現size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
複製代碼
CoreFoundation
硬性規定,一個對象,至少有 16 字節- 以
NSObject
爲例,這裏傳入的size
是alignedInstanceSize
, 而alignedInstanceSize
已經知道 = 8, 8 < 16,retun 16, 最終 NSObject 建立的對象,佔據的內存大小是 16
memory read
查看對象內存(lldb) p obj
(NSObject *) $0 = 0x000060000000eb90
(lldb) memory read 0x000060000000eb90
0x60000000eb90: a8 6e 3a 0b 01 00 00 00 00 00 00 00 00 00 00 00
複製代碼
也能發現,前8 位存儲 isa
指針,後 8 位都是0,可是整個對象仍是佔據了 16 個字節bash
NSObject
對象(malloc_size
函數得到)NSObject
只使用了 8個字節的存儲空間(64bit系統下)class_getInstanceSize()
@interface MNStudent : NSObject
{
int _age;
int _no;
}
@end
複製代碼
NSObject
佔據16個字節可知,base = 16哈哈!中計了!數據結構
原理解釋:app
- 以前
NSObject
建立一個對象,確實是分配了 16 個字節的空間- 可是,他還有未使用的空間8個字節,仍是能夠存儲的
- 這裏的
age
&&no
存進去,正好16
,以前分配的空間正好夠用!因此答案是 16!
(大哥別打了,此次保證不挖坑了,別打,疼,別打臉別打臉。。。)
@interface MNPerson : NSObject
{
int _age;
int _height;
NSString *name;
}
複製代碼
MNPerson
對象,佔用多少內存isa
= 8, int age
= 4, int height
= 4, NSString
= char * = 8哈哈哈哈! 又中計了!
這時候你確定好奇了
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
複製代碼
extraBytes
通常都是 0,這裏能夠理解爲size = alignedInstanceSize()
;alignedInstanceSize = class_getInstanceSize
,class_getInstanceSize
由上圖的log信息也能夠知道 =24
- 心裏os: who tm fucking 32?
下載`libmalloc`,找到`calloc`的實現
void *
calloc(size_t num_items, size_t size)
{
void *retval;
retval = malloc_zone_calloc(default_zone, num_items, size);
if (retval == NULL) {
errno = ENOMEM;
}
return retval;
}
發現這個函數,就是咱們調用的`calloc`函數的底層
void *
malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size)
{
MALLOC_TRACE(TRACE_calloc | DBG_FUNC_START, (uintptr_t)zone, num_items, size, 0);
void *ptr;
if (malloc_check_start && (malloc_check_counter++ >= malloc_check_start)) {
internal_check();
}
ptr = zone->calloc(zone, num_items, size);
if (malloc_logger) {
malloc_logger(MALLOC_LOG_TYPE_ALLOCATE | MALLOC_LOG_TYPE_HAS_ZONE | MALLOC_LOG_TYPE_CLEARED, (uintptr_t)zone,
(uintptr_t)(num_items * size), 0, (uintptr_t)ptr, 0);
}
MALLOC_TRACE(TRACE_calloc | DBG_FUNC_END, (uintptr_t)zone, num_items, size, (uintptr_t)ptr);
return ptr;
}
複製代碼
心裏os: exo me? 這傳入的 size_t size = 24
,怎麼返回32的??
檢索Buckets
- (libmalloc 源碼)
#define NANO_MAX_SIZE 256
/* Buckets sized {16, 32, 48, ..., 256} */
複製代碼
- 發現,iOS 系統分配的時候,有本身的分配規則, 不是說你須要的size多大,就建立多大
- 操做系統內部有本身的一套規則,這裏的都是 16 的倍數,而操做系統在此基礎之上,操做系統的操做訪問最快
- 因此,在
MNPerson
對象須要 24 的size的時候,操做系統根據他的規則,直接建立了 32 的size的空間,因此這裏的答案是 32!
補充說明: sizeof
運算符
(lldb) po [obj class]
MNPerson
(lldb) po sizeof(obj)
8
(lldb) po sizeof(int)
4
複製代碼
sizeof
是運算符,不是函數,編譯時即知道,不是函數- 這裏的
obj
是對象, *obj - 指針指向,編譯器知道他是指針類型,因此 sizeof = 8(指針佔據8個字節) - 特別注意,這裏傳入的不是對象!是指針sizeof
是告訴你傳入的類型,佔多少存儲空間
alloc
出來的對象alloc
都會產生新的instance
對象(內存不相同)instance
對象存儲的信息
class
對象class
對象在內存中存儲的信息
Class metaClass = object_getClass([NSObject class]);
metaclss
是 NSObject
的meta-class
對象meta-class
在內存中只有一份,每一個類都有且只有一個 meta-class
對象meta-class
也是類,與class
的對象結構同樣,可是內部的數據不同(用途不一樣)meta-clas
包括:
提問:object_getClass
與 objc_getClass
的區別
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
複製代碼
instance
對象,返回 class
class
, 返回的是 meta-class
對象meta-class
,返回的是 root-meta-class
對象 Class objc_getClass(const char *aClassName)
{
if (!aClassName) return Nil;
// NO unconnected, YES class handler
return look_up_class(aClassName, NO, YES);
}
複製代碼
(圖片來自於 www.sealiesoftware.com/blog/archiv…)
看懂這張圖 - 就等價於看懂
isa
&&superclass
了
探究流程:
@interface MNSuperclass : NSObject
- (void)superclassInstanceMethod;
+ (void)superClassMethod;
@end
@implementation MNSuperclass
- (void)superclassInstanceMethod{
NSLog(@"superclass-InstanceMethod - %p",self);
}
+ (void)superClassMethod{
NSLog(@"+ superClass-classMethod- %p",self);
}
@end
@interface MNSubclass : MNSuperclass
- (void)subclassInstanceMethod;
@end
@implementation MNSubclass
- (void)subclassInstanceMethod{
NSLog(@"subclassInstanceMethod- %p",self);
}
@end
複製代碼
MNSubclass *subclass = [[MNSubclass alloc]init];
[subclass superclassInstanceMethod];
複製代碼
subclass
調用對象方法,對象方法存在 class
中subclass
對象,經過 isa
指針,找到其對應的 MNSubclass
類MNSubclass
是否有 superclassInstanceMethod
方法的實現,發現沒有,MNSubclass
沿着 superclass
指針找到他的父類 - MNSuperclass
MNSuperclass
中找到 superclassInstanceMethod
的實現,調用它,整個流程結束[MNSubclass superClassMethod];
meta-class
中MNSubclass
,沿着isa
指針,找到其對應的meta-class
MNSubclass
的 meta-class
中是否有 superClassMethod
方法的實現,發現沒有,沿着 superclass
指針找到 MNSuperclass
的 meta-class
MNSuperclass
的 meta-class
有superClassMethod
方法實現,調用,流程結束meta-class
對象存儲的是類方法,class
存儲的是 對象方法Root class
就是 NSObject
, 要給 NSObject
添加方法就要用到 分類
NSObject
的對象方法是否會被調用//"NSObject+MNTest"類的聲明 && 實現
@interface NSObject (MNTest)
+ (void)checkSuperclass;
@end
@implementation NSObject (MNTest)
+ (void)checkSuperclass{
NSLog(@"+NSObject checkSuperclass - %p",self);
}
@end
//main函數中調用
int main(int argc, char * argv[]) {
@autoreleasepool
{
[MNSubclass checkSuperclass];
NSLog(@"MNSubclass = %p",[MNSubclass class]);
}
return 0;
}
--------------------------------------------------------
控制檯輸出:
+NSObject checkSuperclass - 0x105817040
InterView-obj-isa-class[36303:7016608] MNSubclass = 0x105817040
複製代碼
- 發現,調用
checkSuperclass
類方法的,是MNSubclass
類- 這時候要驗證上面那條
meta-class
指向root-class
的線, 這裏的root-class
即等於NSObject
root-class
中只存對象方法,這裏,只要驗證,NSObject
中同名的類方法實現取消,變成同名的對象方法測試,便可得出結論- 聲明的仍是
+ (void)checkSuperclass
,實現的方法用- (void)checkSuperclass
對象方法替換
@interface NSObject (MNTest)
+ (void)checkSuperclass;
@end
@implementation NSObject (MNTest)
//+ (void)checkSuperclass{
// NSLog(@"+NSObject checkSuperclass - %p",self);
//}
- (void)checkSuperclass{
NSLog(@"-NSObject checkSuperclass - %p",self);
}
@end
//main函數中調用
int main(int argc, char * argv[]) {
@autoreleasepool
{
[MNSubclass checkSuperclass];
NSLog(@"MNSubclass = %p",[MNSubclass class]);
}
return 0;
}
--------------------------------------------------------
控制檯輸出:
-NSObject checkSuperclass - 0x101239040
InterView-obj-isa-class[36391:7022301] MNSubclass = 0x101239040
複製代碼
發現 - 調用的仍是類方法 + (void)checkSuperclass
,可是最終實現的,倒是對象方法 - (void)checkSuperclass
[MNSubclass checkSuperclass]
其實本質上,調用的是發送消息方法,函數相似是objc_msgsend([MNSubclass class], @selector(checkSuperclass))
@selector(checkSuperclass)
並未說明是 類方法 or 對象方法root-meta-class
經過super class
找到了root-class
(NSObject),NSObject
類不是元類,存儲的是對象方法,因此 最終調用了NSObject -checkSuperclass
這個對象方法@implementation MNSubclass
- (void)compareSelfWithSuperclass{
NSLog(@"self class = %@",[self class]);
NSLog(@"super class = %@",[super class]);
}
@end
----------------------------------------
調用:
MNSubclass *subclass = [[MNSubclass alloc]init];
[subclass compareSelfWithSuperclass];
複製代碼
問: [self class]
&& [super class]
分別輸出什麼
思路:
class
方法 是NSObject
的一個對象方法,對方方法存在 class
中subclass
對象,經過 isa
指針,找到其對應的 MNSubclass
類MNSubclass
是否有 class
方法的實現,發現沒有,MNSubclass
沿着 superclass
指針找到他的父類 - MNSuperclass
MNSuperclass
中是否有 class
方法的實現,發現沒有,MNSuperclass
沿着 superclass
指針找到他的父類 - NSObject
NSObject
中找到 class
的實現MNSubclass
NSLog(@"self class = %@",[self class]);
NSLog(@"super class = %@",[super class]);
----------------------------------------------------------------------
控制檯輸出:
InterView-obj-isa-class[36796:7048007] self class = MNSubclass
InterView-obj-isa-class[36796:7048007] super class = MNSubclass
複製代碼
感謝小碼哥 的精彩演出,文章中若是有說錯的地方,歡迎提出,一塊兒學習~
歡迎點贊fork~
友情客串: