Effective Objective-C 2.0 筆記

第一章

1. Objective-C 使用的是消息結(jié)構(gòu)而非函數(shù)調(diào)用,其區(qū)別在于:

  • 消息結(jié)構(gòu)的語言镣丑,其運(yùn)行時(shí)所應(yīng)執(zhí)行的代碼由運(yùn)行環(huán)境決定舔糖,編譯完并不知道應(yīng)該執(zhí)行哪個(gè)方法,即運(yùn)行時(shí)綁定
  • 函數(shù)調(diào)用的語言莺匠,由編譯器決定金吗,也就是編譯完就知道應(yīng)該執(zhí)行那個(gè)方法

2.內(nèi)存

NSString *someString = @"The String";

對(duì)象所占內(nèi)存總是分配在堆空間(heap space),而絕不會(huì)分配在棧(stack)上趣竣。

NSString *anotherString = someString;

兩個(gè)變量都指向此實(shí)例摇庙,這說明當(dāng)前棧幀(stack frame)里分配了兩塊內(nèi)存,每塊內(nèi)存的大小都能容下一枚指針(在32位架構(gòu)的計(jì)算機(jī)上是4字節(jié)遥缕,64位是8字節(jié))卫袒,這兩塊內(nèi)存里的值都一樣,就是NSString實(shí)例的內(nèi)存地址单匣。

分配在中的內(nèi)存必須直接管理夕凝,分配在上的用于保存變量的內(nèi)存則會(huì)在其棧幀彈出時(shí)自動(dòng)清理

3.使用對(duì)象會(huì)比使用結(jié)構(gòu)體性能差

對(duì)象還需要額外的開銷,例如分配及釋放堆內(nèi)存等

4.盡量使用@class 前向引用

當(dāng)只需要聲明類型時(shí)封孙,用@class 會(huì)避免編譯.h 里不必要的東西迹冤,從而減少編譯時(shí)間

5.多用字面量語法

NSNumber *someNumber = @7;
id object1 = /*...*/
id object2 = /*...*/
id object3 = /*...*/

NSArray *arrayA = [NSArray arrayWithObjects:object1,object2,object3,nil];
NSArray *arrayB = @[object1,object2,object3];

如果object2 是nil,而arrayB 會(huì)crash 讽营,但是arrayWithObjects 方法會(huì)依次處理各個(gè)參數(shù)虎忌,直到發(fā)現(xiàn)nil 為止,所以最終arrayA = @[ object1 ]橱鹏,類似的方法還有dictionaryWithObjectsAndKeys:

6.多用類型常量膜蠢,少用#define預(yù)處理指令

例如定義一個(gè)動(dòng)畫時(shí)長(zhǎng)

#define ANIMATION_DURATION 0.3

最好替換成

static const NSTimeInterval kAnimationDuration = 0.3;

static表示其作用域僅限于當(dāng)前的目標(biāo)文件中,如果不加的話莉兰,如果其他編譯單元也聲明了同名變量挑围,則編譯器會(huì)拋出異常

如果只是在.m里定義的話,最好加上k 前綴糖荒,如果類外可見的話以類名為前綴

extern NSString* const XXStringConstant; 
NSString* const XXStringConstant = @"XXStringConstant";

此類常量放在全局符號(hào)表里杉辙,這里的XXStringConstant就是一個(gè)“一個(gè)常量,而這個(gè)常量是指針捶朵,指向NSString 對(duì)象

第二章

1.屬性

  • @dynamic : 告訴編譯器不需要生成存取方法
  • @synthesize : 合成屬性 firstName = _firstName;
  • weak : 非擁有的關(guān)系蜘矢,所指對(duì)象遭到摧毀,屬性也會(huì)清空(nil)
  • unsafe_unretained : 當(dāng)所指對(duì)象摧毀時(shí)综看,屬性值不會(huì)自動(dòng)清空

2.等同性

相同的對(duì)象必須具有相同的哈希值品腹,但是兩個(gè)哈希值相同的對(duì)象不一定相同

NSMutableSet * set = [NSMutableSet new];

NSArray *arr1 = @[@1,@2];
NSArray *arr2 = @[@1];

[set addobject:arr1];
[set addobject:arr2];   //set {(1,2),(1)}

[arr2 addObject:@(2)];  //set {(1,2),(1,2)}   哈希值改變了

NSSet *set2 = [set copy];   //  set {(1,2)}    

3.類族模式

類族模式可以把實(shí)現(xiàn)細(xì)節(jié)隱藏在一套簡(jiǎn)單的公共接口后面

+ (UIButton* )buttonWithType:(UIButtonType)type;

該方法所返回的對(duì)象,其類型取決于傳于傳入的按鈕類型红碑,不管是什么類型舞吭,都是繼承與UIButton;

4.在既既有類中使用關(guān)聯(lián)對(duì)象存放自定義數(shù)據(jù) "關(guān)聯(lián)對(duì)象(Associated Object )"

存儲(chǔ)策略 (Storage Policy)

關(guān)聯(lián)類型 等效的@property 屬性
OBJC_ASSOCIATION_ASSIGN assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC nonatomic,retain
OBJC_ASSOCIATION_COPY_NONATOMIC nonatommic,copy
OBJC_ASSOCIATION_RETAIN retain
OBJC_ASSOCIATION_COPY copy
void objc_setAssociatedObject(id object,void*key,id value,objc_AssociationPolicy policy)

以給定的鍵和策略為某對(duì)象設(shè)置關(guān)聯(lián)對(duì)象值

id objc_getAssociatedObject(id object,void*key)

根據(jù)給定的鍵和策略為某對(duì)象設(shè)置關(guān)聯(lián)對(duì)象值

void objc_removeAssociatedObjects(id object)

移除指定對(duì)象的全部關(guān)聯(lián)對(duì)象

key: static void* associatedKey = @"associatedKey"

多次使用alert視圖時(shí),使用此方法較好

5.理解objc_msgSend 的作用

void objc_msgSend(id self, SEL cmd, ...)

這是個(gè)“參數(shù)個(gè)數(shù)可變的函數(shù)”,能接受兩個(gè)或兩個(gè)以上的參數(shù)羡鸥。第一個(gè)參數(shù)代表接收者蔑穴,第二個(gè)參數(shù)代表方法,后續(xù)參數(shù)就是消息中的那些參數(shù)

id returnValue = [someObject messageName:parameter];

編譯器會(huì)將這個(gè)消息轉(zhuǎn)換成如下函數(shù):

id returnValue = objc_msgSend(someObject, @selecter(messageName:), parameter)

該方法需要在接收者所屬的類中搜尋其方法列表惧浴,如果能找到與方法名字相符的方法澎剥,就跳至其實(shí)現(xiàn)代碼,若是找不到赶舆,那就沿著繼承體系繼續(xù)向上查找哑姚,等找到合適的方法之后再跳轉(zhuǎn)。最終還是沒有找到的話芜茵,那就執(zhí)行消息轉(zhuǎn)發(fā)叙量。

objc_msgSend會(huì)將匹配結(jié)果緩存在快速映射表(fast map)里,每個(gè)類都有這樣一塊緩存九串,加快執(zhí)行速度

6.理解消息轉(zhuǎn)發(fā)機(jī)制

在編譯期向類發(fā)送了其無法解讀的消息并不會(huì)報(bào)錯(cuò)绞佩,因?yàn)樵谶\(yùn)行期可以繼續(xù)向類中添加方法,所以編譯器在編譯期還無法確定類中到底會(huì)不會(huì)有某個(gè)方法的實(shí)現(xiàn)猪钮。當(dāng)對(duì)象接收到無法解讀的消息后品山,就會(huì)啟動(dòng)消息轉(zhuǎn)發(fā)機(jī)制,程序員可經(jīng)此過程告訴對(duì)象應(yīng)該如何處理未知消息烤低。

消息轉(zhuǎn)發(fā)分為兩大階段:

  1. 第一階段先征詢接收者肘交,所屬的類,看其是否能動(dòng)態(tài)添加方法扑馁,以處理當(dāng)前這個(gè)未知的選擇子涯呻,這叫做“動(dòng)態(tài)方法解析”
  2. 第二階段涉及“完整的消息轉(zhuǎn)發(fā)機(jī)制”。如果runtime 已經(jīng)把第一階段執(zhí)行完了腻要,那么接收者自己就無法再以動(dòng)態(tài)新增方法的手段來響應(yīng)包含該選擇子的消息了复罐,此時(shí)runtime 會(huì)請(qǐng)求接收者以其他手段來處理該消息。這又分為兩小步:
    首先雄家,請(qǐng)接收者看看有沒有其他對(duì)象能處理這條消息效诅,若有,則runtime 會(huì)把這條消息轉(zhuǎn)給那個(gè)對(duì)象趟济,于是消息轉(zhuǎn)發(fā)結(jié)束乱投,一切如常。若沒有“備援的接收者”咙好,則啟動(dòng)完成的消息轉(zhuǎn)發(fā)機(jī)制篡腌,runtime 會(huì)把與消息有關(guān)的全部細(xì)節(jié)封裝到NSIvocation 對(duì)象中,再給接收者最后一次機(jī)會(huì)勾效,令其設(shè)法解決當(dāng)前還未處理的這條消息

動(dòng)態(tài)方法解析

對(duì)象在收到無法解讀的消息后嘹悼,首先將調(diào)用其所屬類的下列類方法:

+ (BOOL)resolveInstanceMethed:(SEL)selector;

表示這個(gè)類是否能新增一個(gè)實(shí)例方法用以處理此選擇子叛甫。如果是類方法,runtime 會(huì)調(diào)用一個(gè)類似的方法杨伙,叫resolveClassMethod:

備援接收者

第二次處理未知選擇子的機(jī)會(huì)其监,runtime 會(huì)詢問類:能不能把這條消息轉(zhuǎn)給其他接收者來處理。

- (id)forwardingTargetForSelector: (SEL)selector;

若當(dāng)前接收者能找到備援對(duì)象限匣,則將其返回抖苦,若找不到,返回nil米死。通過此方案锌历,我們可以用“組合”來模擬出“多重繼承”的某些特性。在一個(gè)對(duì)象的內(nèi)部峦筒,可能還有一系列其他對(duì)象究西,該對(duì)象可經(jīng)由此方法將能處理選擇子的相關(guān)內(nèi)部對(duì)象返回,在外界看來物喷,好像是該對(duì)象親自處理了消息卤材。

完整的消息轉(zhuǎn)發(fā)

如果轉(zhuǎn)發(fā)算法走到這一步,那么唯一能做的就是啟用完整的消息轉(zhuǎn)發(fā)機(jī)制了峦失,首先創(chuàng)建NSIvocation 對(duì)象扇丛,把與尚未處理的那條消息有關(guān)的全部細(xì)節(jié)封于其中,此對(duì)象包含選擇子尉辑,目標(biāo)及參數(shù)帆精。在觸發(fā)NSInvocation 對(duì)象時(shí),“消息派發(fā)系統(tǒng)”將親自出馬材蹬,把消息指派給目標(biāo)對(duì)象实幕。相當(dāng)于只是改變了調(diào)用目標(biāo)。

-(void)forwardInvocation:(NSInvocation *)invocation;

繼承體系中的每個(gè)類都有機(jī)會(huì)處理此調(diào)用請(qǐng)求堤器,直至NSObject,繼而調(diào)用doesNotRecognizeSelector:以拋出異常,表示最終未能得到處理末贾。

消息轉(zhuǎn)發(fā)全流程

屏幕快照 2016-10-24 下午5.11.39.png

方法調(diào)配技術(shù) (Method swizzling)

類的方法列表會(huì)把選擇子的名稱映射到相關(guān)的方法實(shí)現(xiàn)上闸溃,使得“動(dòng)態(tài)消息派發(fā)系統(tǒng)”能夠根據(jù)此找到應(yīng)該調(diào)用的方法,這些方法均以函數(shù)指針的形式來表示拱撵,這種指針叫做IMP辉川,原型如下:

id (*IMP)(id, SEL, ...)

runtime提供了一些方法來操作這張表

Method originMethod  = class_getInstanceMethod([NSString class], @selector(lowercaseString));
Method swappedMethod = class_getInstanceMethod([NSString class], @selector(uppercaseString));
method_exchangeImplementations(originMethod, swappedMethod);
- (NSString* )my_lowercaseString{
    NSString* lowercaser = [self my_lowercaseString];
    NSLog(@"%@ => %@",self,lowercase);
    return lowercase;
}

將上面方法和NSString 的lowercaseString 方法互換,這段代碼看上去會(huì)死循環(huán)拴测,但是乓旗,在運(yùn)行期間,my_lowercaseString 的選擇子實(shí)際對(duì)應(yīng)的是lowercaseString 集索。通常開發(fā)者可以在調(diào)試階段利用這個(gè)方法來為那些“完全不知道實(shí)現(xiàn)細(xì)節(jié)”的方法增加日志記錄功能屿愚,不宜濫用

7.理解 “類對(duì)象” 的用意

id類型定義如下:

typedef struct objc_object{
    Class isa;// "is a"指針汇跨,定義了對(duì)象所屬的類
} *id;

Class 對(duì)象定義如下:

typedef struct objc_class *Class
struct objc_class{
   Class isa;// "is a"指針,定義了對(duì)象所屬的類
   Class super_class;
   const char *name;
   long version;
   long info;
   long instance_size;
   struct objc_ivar_list *ivars;
   struct objc_method_list **methodLists;
   struct obcj_cache *cache;
   struct objc_protocol_list *protocols;
};

類對(duì)象所屬的類型叫做元類妆距,用來表述類對(duì)象本身所具備的元數(shù)據(jù)穷遂。每個(gè)類既有一個(gè)“類對(duì)象”,而每個(gè)“類對(duì)象”僅有一個(gè)與之相關(guān)的“元類”娱据。

繼承體系如下:

屏幕快照 2016-10-24 下午10.15.08.png

比較對(duì)象是否等同除了 isKindOfClass:還可以用比較類對(duì)象是否相同蚪黑,原因在于,類對(duì)象是“單例”中剩,每個(gè)類的Class 僅有一個(gè)實(shí)例忌穿。

id object = /* ... */;
if ([object class] == [SomeClass class]){
    // 'object is an instance of SomeClass'
}

雖然能這樣比較,但不推薦结啼。例如伴网,某個(gè)對(duì)象可能會(huì)把其收到的所有選擇子都轉(zhuǎn)發(fā)到另一個(gè)對(duì)象。這樣的對(duì)象叫做“代理(proxy)“妆棒,此種對(duì)象均以NSProxy 為根類澡腾。
如果在這種對(duì)象上調(diào)用class 方法,那么返回的是代理對(duì)象本身(此類是NSProxy 的子類)糕珊,而非接受代理的對(duì)象所屬的類动分,然而改用isKindOfClass:就能把這條消息轉(zhuǎn)給接受代理的對(duì)象。

8.Block

塊的內(nèi)部結(jié)構(gòu)

塊本身也是對(duì)象红选,在存放塊對(duì)象的內(nèi)存區(qū)域中澜公,首個(gè)變量是指向Class 對(duì)象的指針,叫做isa 其余內(nèi)存里含有塊對(duì)象正常運(yùn)轉(zhuǎn)所需的各種信息喇肋。

void* isa
int flags
int reserved
void ( ***** )(void ***,...) invoke
struct * descriptor
捕獲到的變量

其中最重要的就是invoke 變量坟乾,這是個(gè)函數(shù)指針,指向塊的實(shí)現(xiàn)代碼蝶防。函數(shù)原型至少要接受一個(gè)void* 型的參數(shù)甚侣,此參數(shù)代表塊。
descriptor 變量是指向結(jié)構(gòu)體的指針间学,其中聲明了塊對(duì)象的總體的大小殷费,還聲明了copy 與dispose 這兩個(gè)輔助函數(shù)所對(duì)應(yīng)的函數(shù)指針。
descriptor 對(duì)應(yīng)結(jié)構(gòu)如下:

unsigned long int reserved
unsigned long int size
void ( ***** )(void ***** , void *****) copy
void ( ***** )(void ***** , void *****) dispose

塊還會(huì)把它所捕獲的所有變量都拷貝一份低葫,這些拷貝都放在descriptor 變量后面详羡,捕獲了多少個(gè)變量,就要占據(jù)多少內(nèi)存空間嘿悬。注意实柠,拷貝的并不是對(duì)象本身,而是指向這些對(duì)象的指針變量善涨。invoke 函數(shù)之所以把塊當(dāng)?shù)谝粋€(gè)參數(shù)傳遞進(jìn)來窒盐,原因在于草则,執(zhí)行塊時(shí),要從內(nèi)存中把這些捕獲的變量讀出來登钥。

全局塊畔师、棧塊及堆塊

定義塊的時(shí)候,其所占內(nèi)存區(qū)域是分配在占棧中牧牢,塊只在定義它的那個(gè)范圍內(nèi)有效看锉。

void (^Block)();
if (/* some condition*/){
    block = ^{
        NSLog(@"Block A");
    };
}else{
    block = ^{
        NSLog(@"Block B");
    }
}
block();

這段代碼就有危險(xiǎn),定義在if 里面的Block 都分配在棧內(nèi)存中塔鳍。在離開了if 語句后伯铣,編譯器有可能把分配在塊的內(nèi)存覆寫掉,可能導(dǎo)致crash轮纫。
為解決此問題腔寡,可以給塊對(duì)象發(fā)送copy 消息來拷貝,這樣可以把塊從棧復(fù)制到堆上掌唾,塊就成了帶引用計(jì)數(shù)的對(duì)象了放前。后續(xù)的拷貝操作都不會(huì)真的執(zhí)行復(fù)制,只是遞增塊對(duì)象的引用計(jì)數(shù)糯彬。

void (^Block)();
if (/* some condition*/){
    block = [^{
        NSLog(@"Block A");
    } copy];
}else{
    block = [^{
        NSLog(@"Block B");
    } copy]
}
block();
全局塊
void (^Block)() = ^{
    NSLog(@"This is a block");   
}

由于在編譯期能確定塊所需的全部信息凭语,所以可以把它做成全局塊。這是種優(yōu)化技術(shù)撩扒。

未完待續(xù)...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末似扔,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子搓谆,更是在濱河造成了極大的恐慌炒辉,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泉手,死亡現(xiàn)場(chǎng)離奇詭異黔寇,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)螃诅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門啡氢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人术裸,你說我怎么就攤上這事⊥ぜ希” “怎么了袭艺?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)叨粘。 經(jīng)常有香客問我猾编,道長(zhǎng)瘤睹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任答倡,我火速辦了婚禮轰传,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瘪撇。我一直安慰自己获茬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布倔既。 她就那樣靜靜地躺著恕曲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪渤涌。 梳的紋絲不亂的頭發(fā)上佩谣,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音实蓬,去河邊找鬼茸俭。 笑死,一個(gè)胖子當(dāng)著我的面吹牛安皱,可吹牛的內(nèi)容都是我干的调鬓。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼练俐,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼袖迎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起腺晾,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤燕锥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后悯蝉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體归形,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年鼻由,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了暇榴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蕉世,死狀恐怖蔼紧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情狠轻,我是刑警寧澤奸例,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站向楼,受9級(jí)特大地震影響查吊,放射性物質(zhì)發(fā)生泄漏谐区。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一逻卖、第九天 我趴在偏房一處隱蔽的房頂上張望宋列。 院中可真熱鬧,春花似錦评也、人聲如沸炼杖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)嘹叫。三九已至,卻和暖如春诈乒,著一層夾襖步出監(jiān)牢的瞬間罩扇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工怕磨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留喂饥,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓肠鲫,卻偏偏與公主長(zhǎng)得像员帮,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子导饲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

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

  • Effective Objective-C讀書筆記捞高,記錄書中的總結(jié)點(diǎn),加入了一些例子渣锦,方便理解和后期回顧硝岗。 一、熟...
    peaktan閱讀 379評(píng)論 0 2
  • 這里僅記錄對(duì)我有幫助的內(nèi)容袋毙,不是對(duì)這本書的全面概括型檀。有些我目前還沒怎么用過的東西,比如GCD胀溺,現(xiàn)在讀來還沒什么感覺...
    倫啊倫閱讀 315評(píng)論 0 1
  • 一 熟悉Objective-C 了解Objective-C語言的起源 在類的頭文件中盡量少引入其他頭文件 除非確有...
    gamper閱讀 240評(píng)論 0 3
  • 對(duì)象,消息扯躺,運(yùn)行期 理解“屬性”這一概念 OC是通過運(yùn)行時(shí)機(jī)制來提供相關(guān)支持的录语,屬性則是用來封裝OC對(duì)象中數(shù)據(jù)的類...
    月子我的嫁閱讀 199評(píng)論 0 0
  • 趕班 一分一秒,時(shí)間迅速臨近7:30蒲稳。再不出發(fā)江耀,上班就要遲到了祥国。 打開門晾腔,...
    笨笨姐妹閱讀 286評(píng)論 3 2