一枚粘、通過源碼理解instance(實(shí)例對象) 荣茫、class object (類對象)厂捞、metaclass(元類對象)
蘋果開源代碼(https://opensource.apple.com/tarballs/)
1.instance 實(shí)例對象
1.1定義
實(shí)例對象是通過類alloc出來的對象,每次調(diào)用alloc都會(huì)產(chǎn)生新的實(shí)例對象
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
以上object1檀葛、object2都是實(shí)例對象玩祟,占用兩塊兒不同的內(nèi)存
1.2、底層實(shí)現(xiàn)
首先屿聋,終端定位到需要轉(zhuǎn)化文件的文件夾下空扎,用以下命令行將OC代碼轉(zhuǎn)成C++/C代碼
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 輸出的CPP文件
以NSObject對象為例,我們用以上方法看看它的底層實(shí)現(xiàn)
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
}
return 0;
}
轉(zhuǎn)成C++代碼后润讥,我們找到了這個(gè)對象的實(shí)現(xiàn)
struct NSObject_IMPL {
Class isa;
};
//Class是指針類型转锈,指向objc_class類型的結(jié)構(gòu)體
typedef struct objc_class *Class;
obj對象內(nèi)部實(shí)際上只有一個(gè)isa指針,指向objc_class類型的結(jié)構(gòu)體楚殿。 那isa指針到底指向誰撮慨,它又有什么用呢? 在下文中我們會(huì)講到
1.3勒魔、更復(fù)雜的繼承結(jié)構(gòu)
我們舉一反三甫煞,設(shè)計(jì)一個(gè)父類Father,繼承于NSObject冠绢,再設(shè)計(jì)一個(gè)子類son繼承于父類抚吠,看看他們的底層實(shí)現(xiàn)
@interface Father : NSObject {
int _age;
}
@end
@interface Son : Father {
double _height;
}
@end
把代碼轉(zhuǎn)成C++,看看內(nèi)部實(shí)現(xiàn)弟胀。直接查找類名_IMPL
struct Father_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
};
struct Son_IMPL {
struct Father_IMPL Father_IVARS;
double _height;
};
上述代碼相當(dāng)于
struct Father_IMPL {
Class isa;
int _age;
};
struct Son_IMPL {
Class isa;
int _age;
double _height;
};
所以實(shí)例對象的本質(zhì)是結(jié)構(gòu)體楷力,(在c++文件中查找類名_IMPL就能找到這個(gè)結(jié)構(gòu)體)喊式,里面有一個(gè)isa指針和其他成員變量。所以實(shí)例對象在內(nèi)存中存儲(chǔ)的內(nèi)容是 isa指針 + 其他成員變量萧朝。
如下圖
我們平時(shí)說打印出來的實(shí)例對象的地址開始就是指的是isa的地址岔留,即isa的地址排在最前面,就是我們實(shí)例對象的地址
2检柬、class-類對象
從第一部分我們了解到献联,實(shí)例對象只存儲(chǔ)isa指針和成員變量,那類里面的方法啊何址,屬性啊等等一些類信息存放在哪里里逆?為什么這些信息沒有存放在實(shí)例對象里?
因?yàn)閷?shí)例對象可能有很多個(gè)用爪,不可能每創(chuàng)建一個(gè)實(shí)例對象都存一份方法原押、屬性.....的。 這些只需要存一份就可以了偎血。一個(gè)類有且只有一個(gè)類對象诸衔。把屬性啊方法啊等信息存在類對象中也再合適不過了。
2.1 創(chuàng)建類對象
NSObject *obj = [[NSObject alloc] init];
Class objClass1 = [NSObject class];
Class objClass2 = [obj class];
Class objClass3 = object_getClass(obj);
我們可以打印一下颇玷,objClass1,objClass12,objClass13他們的內(nèi)存地址都是一樣的笨农。也驗(yàn)證了一個(gè)類有且一有一個(gè)類對象
2.2 類對象的本質(zhì)
如上我們可以看出,類對象中存放了
1亚隙、isa指針
2磁餐、super指針
3、類的屬性信息(@property)阿弃、類的對象方法信息(instance method)
4、類的協(xié)議信息(protocol)羞延、類的成員變量信息(ivar)
........
類對象里面也有一個(gè)isa指針渣淳,還有一個(gè)super指針,那他們分別指向哪里伴箩,又有什么作用呢入愧? 我們稍后就會(huì)講到。 當(dāng)然這里還有一個(gè)疑問嗤谚,既然類對象里面存放的是對象方法信息棺蛛,那類方法信息存放在哪里呢?
3巩步、meta-class-元類對象
構(gòu)建
Class objectMetaClass = object_getClass([NSObject class]);
如上旁赊,objectMetaClass 就是 NSObject的元類對象,并且 每個(gè)類只有一個(gè)元類對象
元類對象和類對象的結(jié)構(gòu)是一樣的椅野,都是objc_class類型的結(jié)構(gòu)體终畅,元類對象存放類方法信息
1籍胯、isa指針
2、super指針
3离福、類的類方法信息(class method)
.........
meta-class對象和class對象的內(nèi)存結(jié)構(gòu)是一樣的杖狼,所以meta-class中也有類的屬性信息,類的對象方法信息等成員變量妖爷,但是其中的值可能是空的蝶涩。
4、isa指針和super指針
實(shí)例對象絮识,類對象绿聘,元類對象中都有isa指針,類對象和元類對象中有super指針笋除。他們分別指向哪里斜友?
4.1、 實(shí)例對象的isa指針指向
eg1:子類Son中有一個(gè)實(shí)例方法- (void)sonTest 垃它,創(chuàng)建一個(gè)實(shí)例對象son鲜屏,然后用這個(gè)對象調(diào)用方法[son sonTest];。
類對象中存儲(chǔ)著實(shí)例方法信息国拇。當(dāng)實(shí)例對象調(diào)用實(shí)例方法時(shí)
實(shí)例方法存儲(chǔ)在類對象中洛史。實(shí)例對象調(diào)用實(shí)例方法時(shí),實(shí)例對象對象通過isa指針找到類對象酱吝,進(jìn)而找到類對象中相應(yīng)的實(shí)例方法進(jìn)行調(diào)用也殖。
實(shí)例對象的isa指針指向它的類對象。
4.2务热、類對象的isa指針指向
元類對象中存儲(chǔ)著類方法信息忆嗜。當(dāng)類對象調(diào)用類方法時(shí),類對象通過isa指針找到元類對象崎岂,進(jìn)而找到元類對象中相應(yīng)的類方法進(jìn)行調(diào)用捆毫,類對象的isa指針指向它的元類對象。
4.3冲甘、元類對象的isa指針指向
元類對象的isa指針指向基類的元類對象(eg:Son的元類對象和Father的元類對象都指向NSObject的元類對象)
4.4绩卤、類對象的super指針指向
eg4:父類Father中有一個(gè)實(shí)例方法- (void)fatherTest ,創(chuàng)建一個(gè)子類實(shí)例對象son江醇,然后用這個(gè)對象調(diào)用方法[son fatherTest];濒憋。
從4.1我們知道,son對象的isa指針會(huì)找到它的類對象陶夜,但是類對象中沒有fatherTest這個(gè)對象方法凛驮,所以類對象會(huì)通過它的super指針找到父類的類對象,而fatherTest這個(gè)方法是存放在Father的類對象中的律适,進(jìn)而調(diào)用辐烂。類對象的super指針是指向父類的類對象的遏插。
特例:當(dāng)這個(gè)類沒有父類時(shí)(基類),則指向nil
4.5纠修、元類對象的super指針指向
元類對象的super指針指向父類的元類對象
特例:基類的元類對象super指針指向基類的類對象
總結(jié)如下:
- instance的isa指向class
- class的isa指向meta-class
- meta-class的isa指向基類的meta-class
- class的superclass指向父類的class
如果沒有父類胳嘲,superclass指針為nil - meta-class的superclass指向父類的meta-class
基類的meta-class的superclass指向基類的class - instance調(diào)用對象方法的軌跡
isa找到class,方法不存在扣草,就通過superclass找父類 - class調(diào)用類方法的軌跡
isa找meta-class了牛,方法不存在,就通過superclass找父類
二辰妙、通過object_getClass探究isa指針指向問題
介紹幾個(gè)函數(shù)
objc_getMetaClass(const char * _Nonnull name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
object_getClass(id _Nullable obj)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
class_isMetaClass(Class _Nullable cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
- (Class)superclass
+ (Class)class
- objc_getMetaClass用于獲取元類對象
- class_isMetaClass用于判斷Class對象是否為元類
- object_getClass用于獲取對象的isa指針指向的對象
- -(Class)superclass 用于獲取對象的superclass指針?biāo)赶虻膶ο?/li>
- +(Class)class 獲取類對象
1.有代碼如下:
// EDStudent 繼承于 EDPerson 鹰祸,EDPerson 繼承于 NSObject
NSObject *object1 = [[NSObject alloc] init]; // 基類 實(shí)例對象
Class object2 = object_getClass(object1); // object1 的isa指針 所指向的對象
Class object3 = object_getClass(object2); // object2 的isa指針 所指向的對象
Class object4 = [NSObject class]; // 基類 類對象
Class object5 = objc_getMetaClass("NSObject"); //基類 元類對象
Class object6 = object_getClass(object5); // 元類對象的isa指針 所指向的對象
NSLog(@"runtimeTest1 %p %p %p %p %p %p %d",object1,object2,object3,object4,object5,object6,class_isMetaClass(object3));
運(yùn)行結(jié)果如下:
runtimeTest1 0x60000311c410 0x7fff86b6c660 0x7fff86b6c638 0x7fff86b6c660 0x7fff86b6c638 0x7fff86b6c638 1
上文我們不難發(fā)現(xiàn),object2 和object4 的內(nèi)存地址是一樣的密浑,object3蛙婴、object5、object6的內(nèi)存地址是一樣的尔破,object4 是類對象街图,object5是元類對象~
也就是說基類的實(shí)例對象object1的isa指針是指向基類的類對象object2(object4),基類的類對象object2的isa指針是指向基類的元類對象object3(object5),基類的元類對象object5的isa指針指向基類的元類對象本身object6(object5)
2.有代碼如下
// EDStudent 繼承于 EDPerson ,EDPerson 繼承于 NSObject
EDPerson *p1 = [[EDPerson alloc] init]; // 實(shí)例對象
Class p2 = object_getClass(p1); // p1 的isa指針 所指向的對象
Class p3 = object_getClass(p2); // p2 的isa指針 所指向的對象
Class p4 = [EDPerson class]; // 類對象
Class p5 = objc_getMetaClass("EDPerson"); // 元類對象
Class p6 = object_getClass(p5); // 元類對象p5的isa指針 所指向的對象
Class objc = objc_getMetaClass("NSObject"); // 基類 元類對象
NSLog(@"runtimeTest2 %p %p %p %p %p %p %p %d",p1,p2,p3,p4,p5,p6,objc,class_isMetaClass(p3));
運(yùn)行結(jié)果如下:
runtimeTest2 0x600003b339a0 0x1066168b0 0x106616888 0x1066168b0 0x106616888 0x7fff86b6c638 0x7fff86b6c638 1
上文我們不難發(fā)現(xiàn)懒构,p2和p4的內(nèi)存地址一樣餐济,p3和p5的內(nèi)存地址一樣,p6和objc內(nèi)存地址一樣胆剧,p4是類對象絮姆,p5是元類對象,objc是基類的元類對象
也就是說 實(shí)例對象的isa指針是指向類對象的秩霍,類對象的isa指針是指向元類對象的篙悯,元類對象的isa指針是指向基類的元類對象
綜上:
實(shí)例對象的isa指針是指向類對象,類對象的isa指針是指向元類對象铃绒,元類對象的isa指針都是指向基類的元類對象
3.有代碼如下
// EDStudent 繼承于 EDPerson 辕近,EDPerson 繼承于 NSObject
Class objc1 = [EDStudent class]; // EDStudent 類對象 objc1
Class objc2 = [objc1 superclass]; // objc1 的 superclass指針 所指向的對象 (EDPerson 類對象) objc2
Class objc3 = [objc2 superclass]; // objc2 的 superclass指針 所指向的對象 (NSObject 類對象) objc3
Class objc4 = [objc3 superclass]; // objc3 的 superclass指針 所指向的對象 (nil) objc4
Class objc5 = [EDPerson class]; // EDPerson 類對象 objc5
Class objc6 = [NSObject class]; // NSObject 類對象 objc6
NSLog(@"runtimeTest3 %p %p %p %p %p %p",objc1,objc2,objc3,objc4,objc5,objc6);
運(yùn)行結(jié)果如下:
runtimeTest3 0x102965950 0x102965900 0x7fff86b6c660 0x0 0x102965900 0x7fff86b6c660
上文我們不難發(fā)現(xiàn),objc2匿垄、objc5內(nèi)存地址一樣,objc3归粉、objc6內(nèi)存地址一樣椿疗,objc4是nil。objc5 是EDPerson類對象糠悼,objc6 是NSObject類對象届榄。
也就是說子類EDStudent的類對象的superclass指針是指向父類EDPerson的類對象,父類EDPerson的類對象的superclass指針是指向基類NSObject的類對象倔喂,基類NSObject的類對象的superclass指針是指向nil
4.有代碼如下
// EDStudent 繼承于 EDPerson 铝条,EDPerson 繼承于 NSObject
Class objc1 = objc_getMetaClass("EDStudent"); // EDStudent 元類對象 objc1
Class objc2 = [objc1 superclass]; // objc1 的 superclass指針 所指向的對象 (EDPerson 元類對象)
Class objc3 = [objc2 superclass]; // objc2 的 superclass指針 所指向的對象 (NSObject 元類對象)
Class objc4 = [objc3 superclass]; // objc3 的 superclass指針 所指向的對象 (NSObject 類對象) 基類的元類對象的superclass指針是指向基類的類對象的
Class objc5 = objc_getMetaClass("EDPerson"); // EDPerson 元類對象 objc5
Class objc6 = objc_getMetaClass("NSObject"); // NSObject 元類對象 objc6
Class objc7 = [NSObject class]; // NSObject 類對象 objc7
NSLog(@"runtimeTest4 %p %p %p %p %p %p %p",objc1,objc2,objc3,objc4,objc5,objc6,objc7);
運(yùn)行結(jié)果如下:
runtimeTest4 0x102965928 0x1029658d8 0x7fff86b6c638 0x7fff86b6c660 0x1029658d8 0x7fff86b6c638 0x7fff86b6c660
上文不難發(fā)現(xiàn)靖苇,objc2、objc5內(nèi)存地址一樣班缰,objc3贤壁、objc6內(nèi)存地址一樣,objc4埠忘、objc7內(nèi)存地址一樣脾拆。objc5是EDPerson元類對象,objc6是NSObject元類對象莹妒,objc7是NSObject類對象
也就是說名船,子類EDStudent的元類對象的superclass指針是指向父類EDPerson的元類對象,父類EDPerson的元類對象的superclass指針指向基類NSObject的元類對象旨怠,基類的元類對象的superclass指針指向基類NSObject的類對象
綜上:
子類的類對象的superclass指針是指向父類的類對象渠驼,父類的類對象的superclass指針指向基類的類對象,基類的類對象的superclass指針是指向nil
子類的元類對象的superclass指針是指向父類的元類對象鉴腻,父類的元類對象的superclass指針是指向基類的元類對象迷扇,基類的元類對象的superclass指針是指向基類的類對象
三、實(shí)例方法和類方法的調(diào)用
有代碼如下:
@interface NSObject (runtime)
- (void)test;
+ (void)test;
@end
@implementation NSObject (runtime)
- (void)test{
NSLog(@"NSObject 實(shí)例方法 Test 執(zhí)行");
}
+ (void)test{
NSLog(@"NSObject 類方法 Test 執(zhí)行");
}
@end
@interface EDPerson : NSObject
@end
@implementation EDPerson
- (void)test {
NSLog(@"EDPerson 實(shí)例方法 Test 執(zhí)行");
}
+ (void)test{
NSLog(@"EDPerson 類方法 Test 執(zhí)行");
}
@end
@interface EDStudent : EDPerson
@end
@implementation EDStudent
- (void)test {
NSLog(@"EDStudent 實(shí)例方法 Test 執(zhí)行");
}
+ (void)test{
NSLog(@"EDStudent 類方法 Test 執(zhí)行");
}
@end
- (void)runtimeTest5 {
[[EDStudent new] test];
[EDStudent test];
}
當(dāng)前控制臺(tái)會(huì)打印“ EDStudent 實(shí)例方法 Test 執(zhí)行 ” 和 “EDStudent 類方法 Test 執(zhí)行”
如果注釋掉EDStudent類的- (void)test 和+ (void)test拘哨,控制臺(tái)會(huì)打印 “EDPerson 實(shí)例方法 Test 執(zhí)行”和“EDPerson 類方法 Test 執(zhí)行”
如果再注釋掉EDPerson類的- (void)test 和+ (void)test谋梭,控制臺(tái)會(huì)打印“NSObject 實(shí)例方法 Test 執(zhí)行” 和 “NSObject 類方法 Test 執(zhí)行”
如果此時(shí)再注釋掉NSObject類的+ (void)test,此時(shí)[EDStudent test] 并不會(huì)報(bào)錯(cuò)倦青,控制臺(tái)會(huì)打印“NSObject 實(shí)例方法 Test 執(zhí)行” 和 “NSObject 實(shí)例方法 Test 執(zhí)行”
綜上:
實(shí)例方法的調(diào)用軌跡:實(shí)例對象通過isa指針找到類對象瓮床,在類對象的方法列表中查找該方法,如果找不到产镐,就通過superclass指針繼續(xù)向上查找隘庄。
類方法的調(diào)用軌跡:類對象通過isa指針找到元類對象,在元類對象的方法列表中查找該方法癣亚,如果找不到丑掺,就通過superclass指針繼續(xù)向上查找。
四述雾、相關(guān)知識(shí)點(diǎn)
在蘋果開源代碼(https://opensource.apple.com/tarballs/)中下載objc4
1. class
代碼如下:
Class objc1 = [EDStudent class];
Class objc2 = [[EDStudent new] class];
NSLog(@"runtimeTest6 %p %p",objc1,objc2);
運(yùn)行結(jié)果:
runtimeTest6 0x10ae388c0 0x10ae388c0
我們不難發(fā)現(xiàn)街州,objc1 和 objc2的內(nèi)存地址是一樣的
為什么呢,我們看下源碼
- object_getClass
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
object_getClass 返回的是當(dāng)前對象的isa指針?biāo)赶虻膶ο?/strong>
- class
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
類方法class玻孟,是返回當(dāng)前類的類對象
實(shí)例方法class唆缴,也是返回當(dāng)前類的類對象。object_getClass(self),是獲取self的isa指針?biāo)赶虻膶ο笫螋幔鴖elf是當(dāng)前類的實(shí)例對象面徽,實(shí)例對象的isa指針是指向類對象,因而此時(shí)object_getClass(self)是獲取當(dāng)前類的對象的。
綜上
class方法是返回當(dāng)前類的類對象
2.superclass
代碼如下:
Class objc1 = [EDStudent superclass];
Class objc2 = [[EDStudent new] superclass];
NSLog(@"runtimeTest7 %p %p",objc1,objc2);
運(yùn)行結(jié)果:
runtimeTest7 0x10ae38870 0x10ae38870
我們不難發(fā)現(xiàn)趟紊,objc1 和 objc2的內(nèi)存地址是一樣的氮双。
來我們繼續(xù)看源碼
- superclass
+ (Class)superclass {
return self->getSuperclass();
}
- (Class)superclass {
return [self class]->getSuperclass();
}
類方法superclass的實(shí)質(zhì)是self->getSuperclass(),此時(shí)self是類對象霎匈,也就是說類方法superclass 返回的是當(dāng)前類的類對象的superclass指針?biāo)赶虻膶ο蟆?br> 實(shí)例方法superclass的實(shí)質(zhì)是[self class]->getSuperclass()戴差,此時(shí)self是實(shí)例對象,[self class]的實(shí)質(zhì)是object_getClass(self)唧躲,因而此時(shí)的[self class] 返回的是實(shí)例對象self的isa指針?biāo)赶虻膶ο笤焱欤串?dāng)前類的類對象,也就是說弄痹,實(shí)例方法superclass 返回的也是當(dāng)前類的類對象的superclass指針?biāo)赶虻膶ο蟆?/strong>
綜上
superclass 方法是返回當(dāng)前類的類對象的superclass指針?biāo)赶虻膶ο?/strong>
3.isKindOfClass 和 isMemberOfClass
代碼如下:
BOOL res1 = [[EDStudent new] isKindOfClass:[EDStudent class]];
BOOL res2 = [[EDStudent class] isKindOfClass:[EDStudent class]];
BOOL res3 = [[NSObject class] isKindOfClass:[NSObject class]];
NSLog(@"runtimeTest8 %d %d %d",res1,res2,res3);
運(yùn)行結(jié)果:
runtimeTest8 1 0 1
代碼如下:
BOOL res1 = [[EDStudent new] isMemberOfClass:[EDStudent class]];
BOOL res2 = [[EDStudent class] isMemberOfClass:[EDStudent class]];
BOOL res3 = [[NSObject class] isMemberOfClass:[NSObject class]];
NSLog(@"runtimeTest9 %d %d %d",res1,res2,res3);
運(yùn)行結(jié)果:
runtimeTest9 1 0 0
是不是和我們平時(shí)的認(rèn)知有些不一樣饭入,別急,想要理解為什么是這樣肛真,我們來看下源碼
- isKindOfClass
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
在源碼上谐丢,我們不難發(fā)現(xiàn),isKindOfClass的類方法和實(shí)例方法的區(qū)別在于for循環(huán)的初始值tcls蚓让,類方法isKindOfClass的for循環(huán)初始值是tcls = self->ISA()乾忱,初始值tcls是當(dāng)前對象的isa指針?biāo)赶虻膶ο螅瑢?shí)例方法isKindOfClass的for循環(huán)初始值是tcls = [self class],由于是實(shí)例方法历极,因而[self class]可以改寫為object_getClass(self)窄瘟,而object_getClass表示當(dāng)前對象的isa指針?biāo)赶虻膶ο?因而isKindOfClass的類方法和實(shí)例方法for循環(huán)的初始值tcls都是當(dāng)前對象的isa指針?biāo)赶虻膶ο?/strong>
綜上
isKindOfClass方法是一個(gè)for循環(huán)查找,由當(dāng)前對象的isa指針?biāo)赶虻膶ο螅╰cls = self->ISA() 或 tcls = [self class])開始趟卸,通過superclass指針(tcls = tcls->getSuperclass())向上查找蹄葱,如果找到的對象和cls相同就返回YES,否則返回NO
- isMemberOfClass
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
和 isKindOfClass 類似锄列,實(shí)例方法isMemberOfClass 中的[self class] 可以改寫為object_getClass(self)图云,而object_getClass(self)表示當(dāng)前對象的isa指針?biāo)赶虻膶ο螅蚨鴖elf->ISA() 和 [self class] 都是表示當(dāng)前對象的isa指針?biāo)赶虻膶ο?/strong>
綜上
isMemberOfClass方法是判斷當(dāng)前對象的isa指針?biāo)赶虻膶ο蠛蚦ls是否相同
看到這邻邮,不知道大家有沒有一個(gè)小疑問竣况,當(dāng)前類的類對象,不管是通過何種方式獲取的筒严,它們的內(nèi)存地址都是一樣的丹泉,當(dāng)前類的元類對象,也是如此~
類對象保存的是創(chuàng)建實(shí)例對象所需要的信息(實(shí)例方法 協(xié)議等)鸭蛙,因而它只需要一份嘀掸,不一定是單例
元類對象保存的是創(chuàng)建類對象所需的信息(類方法),因而它也只需一份规惰,不一定是單例