Objective-C底層面試題總結(jié)

方法的歸屬問(wèn)題探索

  1. 定一個(gè)Person類碉渡,定義一個(gè)實(shí)例方法,一個(gè)類方法母剥,并完成實(shí)現(xiàn)
@interface Person : NSObject
- (void)sayHello;
+ (void)sayHappy;

@end

@implementation Person

- (void)sayHello{
    NSLog(@"Person say : Hello!!!");
}

+ (void)sayHappy{
    NSLog(@"Person say : Happy!!!");
}

@end
  1. 獲取類的方法并打印出來(lái)
void objc_copyMethodList(Class pClass){
    unsigned int count = 0;
    Method *methods = class_copyMethodList(pClass, &count);
    for (unsigned int i=0; i < count; i++) {
        Method const method = methods[i];
        //獲取方法名
        NSString *key = NSStringFromSelector(method_getName(method));
        
        NSLog(@"Method, name: %@", key);
    }
    free(methods);
}
  1. 獲取實(shí)例方法并打印其地址
void instanceMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
    Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));

    Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
    
    NSLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
  1. 獲取類方法并打印其地址
void classMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getClassMethod(pClass, @selector(sayHello));
    Method method2 = class_getClassMethod(metaClass, @selector(sayHello));

    Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
    // 元類 為什么有 sayHappy 類方法 0 1
    //
    Method method4 = class_getClassMethod(metaClass, @selector(esd));
    
    NSLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
  1. 獲取方法實(shí)現(xiàn)
void IMP_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);

    // - (void)sayHello;
    // + (void)sayHappy;
    IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
    IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));

    IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));

    NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
    NSLog(@"%s",__func__);
}
  1. 方法調(diào)用
        Person *person = [Person alloc];
        Class pClass     = object_getClass(person);
        objc_copyMethodList(pClass);

        instanceMethod_classToMetaclass(pClass);
        classMethod_classToMetaclass(pClass);
        IMP_classToMetaclass(pClass);
  1. 執(zhí)行結(jié)果
Method, name: sayHello
instanceMethod_classToMetaclass - 0x1000081b0-0x0-0x0-0x100008148
classMethod_classToMetaclass-0x0-0x0-0x100008148-0x0
IMP_classToMetaclass-0x100003d10-0x7fff67dc7bc0-0x7fff67dc7bc0-0x100003d40

接下來(lái)我們逐個(gè)函數(shù)進(jìn)行分析滞诺,然后論證結(jié)果

objc_copyMethodList

  • class_copyMethodList(Class _Nullable cls, unsigned int * _Nullable outCount)
    runtime提供的函數(shù),用來(lái)獲取類的方法列表环疼,并返回方法個(gè)數(shù)铭段。

從這個(gè)方法里面我們知道,Person中只存儲(chǔ)了實(shí)例方法

class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)

  • command + shift + 0先查看官方文檔


    class_getInstanceMethod文檔.png

從文檔中我們可以看出秦爆,當(dāng)查找實(shí)例方法的時(shí)候,會(huì)沿著當(dāng)前類的繼承鏈找下去憔披。

  • method1
    • Person類中找sayHello實(shí)例方法等限,能找到,所以地址有值
  • method2
    • MetaClass(Person)的中找sayHello實(shí)例方法芬膝,找不到
    • 然后沿著MetaClass→SuperMetaClass→RootMetaClass→NSObject→nil繼承鏈找望门,直到nil都沒(méi)找到sayHello實(shí)例方法,所以打印method2的地址為0x0
  • method3
    • Person類中找sayHappy類方法锰霜,沒(méi)有找到
    • 然后沿著Person→NSObject→nil繼承鏈找筹误,直到nil都沒(méi)找到sayHappy類方法,所以答應(yīng)method2的地址為0x0
  • method4
    • MetaClass(Person)的中找sayHappy類方法癣缅,能找到厨剪,所以打印的地址有值

class_getClassMethod(Class _Nullable cls, SEL _Nonnull name)

  • 先看看文檔怎么說(shuō)


    class_getClassMethod文檔.png

從文檔上我們可以看出,也是通過(guò)父類是否有class method來(lái)判斷

  • class_getClassMethod源碼
Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    return class_getInstanceMethod(cls->getMeta(), sel);
}

Class getMeta() {
    if (isMetaClass()) return (Class)this;
    else return this->ISA();
}

看了源碼友存,其實(shí)是去獲取元類的實(shí)例化方法祷膳。如果當(dāng)前類是元類就直接使用當(dāng)前類,來(lái)完成class_getInstanceMethod屡立,這里不繼續(xù)通過(guò)→ISA()來(lái)繼續(xù)往下摸直晨,是因?yàn)闀?huì)產(chǎn)生無(wú)限循環(huán)。我們知道class_getInstanceMethod會(huì)根據(jù)繼承鏈一直去尋找實(shí)例方法膨俐。

  • method1:
    • 首先獲取Person的元類MetaClass(Person)勇皇,然后尋找沿著MetaClass→SuperMetaClass→RootMetaClass→NSObject→nil去尋找sayhello類的實(shí)例方法,最終都找不到焚刺,所以地址為0x0敛摘。
  • method2:
    • 首先獲取MetaClass(Person)的元類,因?yàn)?code>MetaClass(Person)已經(jīng)是元類了檩坚,所以直接返回MetaClass(Person)着撩,然后尋找沿著MetaClass→SuperMetaClass→RootMetaClass→NSObject→nil去尋找sayhello類的實(shí)例方法诅福,最終都找不到,所以地址為0x0拖叙。
  • method3:
    • 首先獲取Person的元類MetaClass(Person)氓润,然后尋找沿著MetaClass→SuperMetaClass→RootMetaClass→NSObject→nil去尋找Person的元類繼承鏈的sayHappy實(shí)例方法,通過(guò)前面的分析我們知道Person的類方法sayHappy就存在MetaClass(Person)中薯鳍,所以返回打印的地址有值咖气。
  • method4:
    • 首先獲取MetaClass(Person)的元類,因?yàn)?code>MetaClass(Person)已經(jīng)是元類了挖滤,所以直接返回MetaClass(Person)崩溪,然后尋找沿著MetaClass→SuperMetaClass→RootMetaClass→NSObject→nil去尋方法名為sayHappy的方法,通過(guò)前面的分析我們知道sayHappy就存在MetaClass(Person)中斩松,所以返回打印的地址有值伶唯。

class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name)

  • class_getMethodImplementation文檔


    class_getMethodImplementation文檔.png
    • 該方法會(huì)返回一個(gè)指向方法實(shí)現(xiàn)的函數(shù)指針
    • 速度比method_getImplementation(class_getInstanceMethod(cls, name))
    • 返回的函數(shù)指針不一定是該方法的實(shí)現(xiàn),也可能是runtime的一個(gè)內(nèi)部函數(shù)
    • 如果類實(shí)例無(wú)法響應(yīng)selector惧盹,則返回的函數(shù)指針將是runtime消息轉(zhuǎn)發(fā)機(jī)制的一部分
  • 再來(lái)看看源碼
IMP class_getMethodImplementation(Class cls, SEL sel)
{
    IMP imp;

    if (!cls  ||  !sel) return nil;

    imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);

    // Translate forwarding function to C-callable external version
    if (!imp) {
        return _objc_msgForward;
    }

    return imp;
}

通過(guò)源碼乳幸,首先去查找方法實(shí)現(xiàn),如果沒(méi)有找到钧椰,則進(jìn)行消息轉(zhuǎn)發(fā)粹断。

  • imp1:
    • 通過(guò)前面我們知道,sayhello實(shí)例方法嫡霞,存在Person類中瓶埋,能找到,所以返回函數(shù)指針诊沪,能打印出其地址
  • imp2:
    • sayhello不存在元類中养筒,所以進(jìn)行了消息轉(zhuǎn)發(fā)
  • imp3:
    • sayHappy是類方法,不存在類中娄徊,所以進(jìn)行了消息轉(zhuǎn)發(fā)
  • imp4:
    • sayhappy是類方法闽颇,存在元類中,能在元類中找到寄锐,所以返回函數(shù)指針兵多,能打印出其地址

iskindOfClass & isMemberOfClass

  • 類方法isKindOfClassisMemberOfClass
    BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
    BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
    BOOL re3 = [(id)[Person class] isKindOfClass:[Person class]];
    BOOL re4 = [(id)[Person class] isMemberOfClass:[Person class]];
    NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);

輸出如下

 re1 :1
 re2 :0
 re3 :0
 re4 :0
  • 實(shí)例方法isKindOfClassisMemberOfClass
    BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
    BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
    BOOL re7 = [(id)[Person alloc] isKindOfClass:[Person class]];
    BOOL re8 = [(id)[Person alloc] isMemberOfClass:[Person class]];

輸出:

 re5 :1
 re6 :1
 re7 :1
 re8 :1

為什么會(huì)是這個(gè)結(jié)果,我們來(lái)分析一下isKindOfClassisMemberOfClass的源碼

  • isKindOfClass
  1. isKindOfClass類方法源碼
+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

通過(guò)源碼我們可以看到橄仆,當(dāng)isKindOfClass作為類方法的時(shí)候剩膘,是先通過(guò)ISA獲取當(dāng)前類的元類,然后遞歸當(dāng)前的元類繼承鏈和當(dāng)前類進(jìn)行對(duì)比盆顾。

  • re1:
    • NSObject→ISA():RootMetaClass VS NSObject怠褐,不滿足,循環(huán)繼續(xù)
    • 根元類superclassNSObject VS NSObject您宪。返回YES
  • re3
    • Peson→ISA():MetaClass(Person) VS Peson奈懒,不滿足奠涌,循環(huán)繼續(xù)
    • Meta(Person)→superclass:SuperMetaClass(Peson) VS Person, 不滿足,循環(huán)繼續(xù)
    • SuperMeta(Peson)→superclass:RootMetaClass VS Person, 不滿足磷杏,循環(huán)繼續(xù)
    • RootMetaClass→superclass:NSObject VS Person, 不滿足溜畅,循環(huán)繼續(xù)
    • NSObject→superclass:nil,跳出循環(huán)极祸,返回NO

  1. isKindOfClass實(shí)例方法源碼
- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}
// class的實(shí)例方法
- (Class)class {
    return object_getClass(self);
}

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

objc_object::getIsa() 
{
    if (fastpath(!isTaggedPointer())) return ISA();

    extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
    uintptr_t slot, ptr = (uintptr_t)this;
    Class cls;

    slot = (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
    cls = objc_tag_classes[slot];
    if (slowpath(cls == (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer)) {
        slot = (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
        cls = objc_tag_ext_classes[slot];
    }
    return cls;
}

通過(guò)源碼慈格,當(dāng)isKindOfClass作為實(shí)例方法的時(shí)候,先獲取當(dāng)前實(shí)例對(duì)象的isa當(dāng)前實(shí)例的類遥金,與傳入類進(jìn)行對(duì)比浴捆,然后再把傳入類與當(dāng)前實(shí)例對(duì)象的類繼承鏈來(lái)進(jìn)行對(duì)比

  • re5:
    • [NSObject alloc]→getIsa() : NSObject VS NSObject,返回YES
  • re7:
    • [Person alloc]→getIsa() : Person VS VS Person稿械,返回YES选泻。

!C滥滔金!注意這里有坑點(diǎn)
看上去分析的很對(duì),實(shí)際上是這樣的么茂嗓?我們進(jìn)行斷點(diǎn)調(diào)試的時(shí)候發(fā)現(xiàn)isKindOfClass不走我們上面分析的兩個(gè)方法,而是統(tǒng)一走objc_opt_isKindOfClass

isKindOfClass的調(diào)用.png

??接著我們來(lái)分析objc_opt_isKindOfClass的源碼

BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
    if (slowpath(!obj)) return NO;
    Class cls = obj->getIsa();
    if (fastpath(!cls->hasCustomCore())) {
        for (Class tcls = cls; tcls; tcls = tcls->superclass) {
            if (tcls == otherClass) return YES;
        }
        return NO;
    }
#endif
    return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}

看到源碼科阎,是不是和上面分析的是基本一致的述吸,首先通過(guò)需要判斷的對(duì)象或者類的isa來(lái)獲取當(dāng)對(duì)象的類或者類的元類,然后通過(guò)他們的繼承鏈來(lái)與需要對(duì)比的類進(jìn)行對(duì)比
Tips: 類通過(guò)isa可以獲取元類锣笨,對(duì)象通過(guò)isa獲取實(shí)例當(dāng)前對(duì)象的類

  • isMemberOfClass
    為了避免剛剛的問(wèn)題蝌矛,我們首先來(lái)看下isMemberOfClass方法的調(diào)用走哪個(gè)方法
    isMemberOfClass方法流程.png

    確實(shí)會(huì)走isMemberOfClass的底層方法,可以放心分析了
  • isMemberOfClass類方法的源碼
+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}

通過(guò)源碼错英,拿元類和傳入類來(lái)進(jìn)行對(duì)比

  • r2:
    • NSObject→ISA(): RootMetaClass VS NSObject入撒,返回NO
  • r4:
    • Person→ISA(): MetaClass(Person) VS NSObject,返回NO

  • isMemberOfClass實(shí)例方法的源碼
- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}

// class的實(shí)例方法
- (Class)class {
    return object_getClass(self);
}

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

objc_object::getIsa() 
{
    if (fastpath(!isTaggedPointer())) return ISA();

    extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
    uintptr_t slot, ptr = (uintptr_t)this;
    Class cls;

    slot = (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
    cls = objc_tag_classes[slot];
    if (slowpath(cls == (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer)) {
        slot = (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
        cls = objc_tag_ext_classes[slot];
    }
    return cls;
}

分析源碼椭岩,[self class]其實(shí)就是通過(guò)isa去獲取實(shí)例化當(dāng)前對(duì)象的類茅逮,然后與傳入類來(lái)進(jìn)行對(duì)比。

  • r6:
    • [NSObject alloc]→getIsa(): NSObject VS NSObject判哥,返回YES
  • r4:
    • [Person alloc]→getIsa(): Person VS Person献雅,返回YES
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市塌计,隨后出現(xiàn)的幾起案子挺身,更是在濱河造成了極大的恐慌,老刑警劉巖锌仅,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件章钾,死亡現(xiàn)場(chǎng)離奇詭異墙贱,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)贱傀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門惨撇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人窍箍,你說(shuō)我怎么就攤上這事串纺。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)邻梆。 經(jīng)常有香客問(wèn)我窖杀,道長(zhǎng),這世上最難降的妖魔是什么爽蝴? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上巨朦,老公的妹妹穿的比我還像新娘。我一直安慰自己剑令,他們只是感情好糊啡,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著吁津,像睡著了一般棚蓄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上碍脏,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天梭依,我揣著相機(jī)與錄音,去河邊找鬼典尾。 笑死役拴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的钾埂。 我是一名探鬼主播河闰,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼褥紫!你這毒婦竟也來(lái)了淤击?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤故源,失蹤者是張志新(化名)和其女友劉穎污抬,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡印机,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年矢腻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片射赛。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡多柑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出楣责,到底是詐尸還是另有隱情竣灌,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布秆麸,位于F島的核電站初嘹,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏沮趣。R本人自食惡果不足惜屯烦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望房铭。 院中可真熱鬧驻龟,春花似錦、人聲如沸缸匪。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)凌蔬。三九已至谴蔑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間龟梦,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工窃躲, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留计贰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓蒂窒,卻偏偏與公主長(zhǎng)得像躁倒,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子洒琢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1.objc_object 與 對(duì)象的關(guān)系 所有的OC對(duì)象都是以objc_object為模板繼承過(guò)來(lái)的所有的對(duì)象都...
    全球通_2017閱讀 582評(píng)論 0 3
  • 題一 打印結(jié)果: 首先傳入?yún)?shù)pClass秧秉,是LGPerson類。lgInstanceMethod_classTo...
    8ef7f923f5bb閱讀 289評(píng)論 0 1
  • 面試題一: 元類 中為什么會(huì)有 類對(duì)象 的 類方法衰抑? 在[http://www.reibang.com/p/49...
    安靜的潑猴閱讀 269評(píng)論 0 3
  • 這篇文章主要是從源碼的角度分析 class_getInstanceMethod方法和class_getClassM...
    Bel李玉閱讀 220評(píng)論 0 3
  • 久違的晴天象迎,家長(zhǎng)會(huì)。 家長(zhǎng)大會(huì)開(kāi)好到教室時(shí),離放學(xué)已經(jīng)沒(méi)多少時(shí)間了砾淌。班主任說(shuō)已經(jīng)安排了三個(gè)家長(zhǎng)分享經(jīng)驗(yàn)啦撮。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,493評(píng)論 16 22