第二章 對象消息運行期(EffectiveObjective-C讀書筆記)

1.屬性

使用屬性,編譯器在編譯期,自動合成,setter方法,getter方法與帶下劃線的實例變量

屬性 = setter + getter + 實例變量

如果自己實現(xiàn)了其中一種方法,那么另外一種方法編譯器來合成

如果不想編譯器自動合成,可以使用關鍵字@dynamic. 這個時候不會自動生成setter與getter的實現(xiàn)(會生成聲明)與屬性.而且編譯期間也不會報錯

assign:使用assign來修飾對象類型的時候,當目標對象銷毀的時候,屬性值不會自動設置為nil

weak:使用weak來修飾對象類型的時候,當目標對象銷毀的時候,屬性值會自動設置為nil

unsafe_unretained:特質與assign相同,但是它只適用于"對象類型",對象銷毀不會自動設置為nil

2.在對象內部盡量直接訪問實例變量

建議:除特殊情況之外

讀取實例變量的時候采用直接訪問的形式(例如懶加載除外)

設置實例變量的時候通過屬性來做

由于不經(jīng)過OC的方法派發(fā),所以直接訪問實例變量的速度比較快,這種情況下,編譯器所生成的代碼會直接訪問保存對象實例變量的那塊內存

3.理解"對象等同性"這一概念

NSString *one = @"123";

NSString *two = [NSString stringWithFormat:@"123"];

NSLog(@"%p,%p",one,two);//0x10f844078,0xa000000003332313

NSLog(@"%d",(one == two));//0

NSLog(@"%d",[one isEqual:two]);//1

NSLog(@"%d",[one isEqualToString:two]);//1

NSLog(@"%lu",(unsigned long)[one hash]);//487557729

NSLog(@"%lu",(unsigned long)[two hash]);//487557729

4.以"類簇模式"隱藏實現(xiàn)細節(jié)

可以隱藏"抽象基類"背后的實現(xiàn)細節(jié), 比如UIKit中的UIButton類,可以通過

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

該方法返回的對象,類型取決與傳入的buttonType.然后不管是什么類型,他們都繼承基類UIButton,

好處在于:UIButton類的使用者無需關系創(chuàng)建出來的按鈕具體屬于哪個子類

4.1創(chuàng)建類簇舉例

typedef NS_ENUM(NSUInteger,EOCEmployeeType) {

EOCEmployeeTypeDeveloper,

EOCEmployeeTypeDesigner,

EOCEmployeeTypeFinance

};

@interface EOCEmployee : NSObject

@property (nonatomic, copy) NSString *name;

@property (nonatomic, assign) NSUInteger salary;

+ (EOCEmployee *)employeeWithType:(EOCEmployeeType)type;

- (void)doADayWorks;

@end

#import "EOCEmployee.h"

#import "EOCEmployeeDeveloper.h"

#import "EOCEmployeeDesign.h"

@implementation EOCEmployee

+ (EOCEmployee *)employeeWithType:(EOCEmployeeType)type{

? ? switch (type) {

? ? ? ? case EOCEmployeeTypeDeveloper:

? ? ? ? ? ? return [[EOCEmployeeDeveloper alloc] init];

? ? ? ? ? ?break;

? ? ? case EOCEmployeeTypeDesigner:

? ? ? ? ? return [[EOCEmployeeDesign alloc] init];

? ? ? ? ? break;

? ? ?case EOCEmployeeTypeFinance:

? ? ? ? return nil;

? ? ? ?break; ?

? ?}

}

- (void)doADayWorks{

//subclasses implement this

}

4.2子類實現(xiàn)

#import "EOCEmployeeDeveloper.h"

@implementation EOCEmployeeDeveloper

- (void)doADayWorks{

NSLog(@"write code");

}

@end

4.3使用

EOCEmployee *employeeDev = [EOCEmployee employeeWithType:EOCEmployeeTypeDeveloper];

[employeeDev doADayWorks];

EOCEmployee *employeeDes = [EOCEmployee employeeWithType:EOCEmployeeTypeDesigner];

[employeeDes doADayWorks];

5.在既有類中使用關聯(lián)對象存放自定義數(shù)據(jù)

我們通常會使用子類來實現(xiàn),但是有的類并不能通過繼承來解決, 可以通過Associated Object關聯(lián)對象來實現(xiàn)

void objc_setAssociatedObject(idobject,constvoid*key,idvalue, objc_AssociationPolicy policy)

id objc_getAssociatedObject(idobject,constvoid*key)

void objc_removeAssociatedObjects(idobject)

eg

UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"警告" message:@"地球危險" delegate:self cancelButtonTitle:@"知道了" otherButtonTitles:@"回去火星", nil];

Block block = ^(NSInteger index){

? ? if (index == 1){

? ? ? NSLog(@"地球太危險,跟我回火星");

? ? }else{

? ? ? NSLog(@"我是奧特曼我怕誰");

? ? }

};

objc_setAssociatedObject(alertView, "kAlertViewBlockKey", block, OBJC_ASSOCIATION_COPY_NONATOMIC);

[alertView show];

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{

? ? Block block = objc_getAssociatedObject(alertView, "kAlertViewBlockKey");

? ? block(buttonIndex);

}

6.理解objc_msgSend

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

第一個參數(shù):消息接受者

第二個參數(shù):選擇器

…:后面的為參數(shù)(如果有)

發(fā)送消息:首先接受者去所屬類的list of methods中查找與名稱相符的方法,然后跳轉到實現(xiàn)代碼如果沒有找到,沿著繼承體系去找,找到再跳轉,最終都沒找到,則執(zhí)行message forwarding

注意:objc_msgSend會將匹配結果緩存到fast map.每個類都有一個緩存,稍后還想該類發(fā)送與選擇器相同的方法那么執(zhí)行起來就會更快

7.理解消息轉發(fā)機制

在編譯器向類發(fā)送了其無法解讀的消息并不會報錯,因為在運行可以繼續(xù)向類中添加方法

運行期當對象接受到無法解讀的消息就會啟動message forwarding

//unrecognized selector sent to instance 0x87...

第一階段:動態(tài)方法解析 dynamic method resolution

第二階段:完整的消息轉發(fā)機制 ?full forwading mechanism

1>.動態(tài)方法解析

對象在收到無法解讀的消息后,首先調用下列類方法

+ (BOOL)resolveInstanceMethod:(SEL)selector

//selector為未知的選擇器,

//返回值BOOL類型,表示這個類是否能新增一個實例方法用于處理選擇器

//如果未實現(xiàn)的方法不是實例方法而是類方法, 則會調用"resolveClassMethod"

注意:使用這種辦法的前提,相關方法的實現(xiàn)代碼已經(jīng)寫好,只等著運行的時候動態(tài)插在類里面就可以

此方案常用來實現(xiàn)@dynamic.

2>備用接受者

當前接受者有第二次機會能處理未知的選擇器,這一步,運行系統(tǒng)會問:能否把這條消息轉給其他接受者處理

- (id)forwardingTargetForSelector:(SEL)sector

通過該方法可以通過組合來模擬"多重繼承".在一個對象內部,有一系列其他對象,該對象可以在此方法將能處理某選擇器的相關對象返回

3>完整的消息轉發(fā)

如果forwardingTargetForSelector返回nil,則會到完整的消息轉發(fā),首先創(chuàng)建一個NSInvocation對象,此對象包含選擇器(selector),目標(target),參數(shù).然后執(zhí)行

- (void)forwardInvocation:(NSInvocation *)invocation

①.這個方法可以實現(xiàn)的很簡單,只需改變調用目標,是消息在新目標上執(zhí)行,這樣做與"備用接受者"等效,無意義②.在觸發(fā)消息之前,先以某種方式,改變消息內容,比如更好選擇器,追加參數(shù)等

8.用"方法調配技術" 調試"黑盒方法"

在運行期間,可以向類中新增或者替換選擇器所對應的方法實現(xiàn)

使用一份實現(xiàn)來替換原有的方法實現(xiàn),叫做"方法調配",常用此技術向原有實現(xiàn)中添加新功能

MethodoriginalMethod =class_getInstanceMethod([NSStringclass],@selector(uppercaseString));

MethodswappedMethod =class_getInstanceMethod([NSStringclass],@selector(lowercaseString));

method_exchangeImplementations(originalMethod, swappedMethod);

9.理解"類對象"的用意

編譯器無法確定某類型對象到底能解讀多少種選擇器,因為運行期還可以向其中動態(tài)新增

"在運行期檢視對象類型"叫做"類型信息查詢"(內省).這個特性內置于Foundation框架

的NSObject協(xié)議中,凡是由公共根類繼承來的對象都要遵守這個協(xié)議

id 類型是objc_object *類型的指針

typedefstructobjc_object *id;

objc_object對象結構體有個Class類的的變量isa. 該變量定義了對象所屬的類.

structobjc_object {

Class isa? OBJC_ISA_AVAILABILITY;

};

Class類型是objc_class類型的結構體指針

typedefstructobjc_class *Class;

objc_class結構體存放"元數(shù)據(jù)",例如類的實例實現(xiàn)了幾個方法,具備多少個實例變量等消息

該結構體的第一個變量也是isa指針,說明Class本身也是Objective-C對象.

super_class定義了本類的超類,類對象所屬的類型(isa指向的類)是另外一個類,叫元類,用來描述類對象的元數(shù)據(jù)

"類方法"就定義與此處,這些方法可以理解為類對象的實例方法.每個類只有一個類對象

structobjc_class {

Class isa? OBJC_ISA_AVAILABILITY;

Class super_class??????????????????????????????????????? OBJC2_UNAVAILABLE;

constchar*name ??????????????????????????????????????? OBJC2_UNAVAILABLE;

longversion ??????????????????????????????????????????? OBJC2_UNAVAILABLE;

longinfo??????????????????????????????????????????????? OBJC2_UNAVAILABLE;

longinstance_size ????????????????????????????????????? OBJC2_UNAVAILABLE;

structobjc_ivar_list *ivars ??????????????????????????? OBJC2_UNAVAILABLE;

structobjc_method_list **methodLists??????????????????? OBJC2_UNAVAILABLE;

structobjc_cache *cache ??????????????????????????????? OBJC2_UNAVAILABLE;

structobjc_protocol_list *protocols ??????????????????? OBJC2_UNAVAILABLE;

}

1>在類繼承體現(xiàn)中查詢類型消息

- (BOOL)isKindOfClass:(Class)aClass//判斷對象是否為某個類或者派生類的實例

- (BOOL)isMemberOfClass:(Class)aClass//判斷對象是否為某個特定類的實例

盡量使用類型信息查詢方法來確定對象類型,而不是直接比較類對象("=="). 因為某些對象可能實現(xiàn)了消息轉發(fā)功能

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末织咧,一起剝皮案震驚了整個濱河市沪曙,隨后出現(xiàn)的幾起案子浮禾,更是在濱河造成了極大的恐慌,老刑警劉巖燕鸽,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡菜皂,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門厉萝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來恍飘,“玉大人,你說我怎么就攤上這事谴垫≌履福” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵翩剪,是天一觀的道長乳怎。 經(jīng)常有香客問我,道長肢专,這世上最難降的妖魔是什么舞肆? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮博杖,結果婚禮上椿胯,老公的妹妹穿的比我還像新娘。我一直安慰自己剃根,他們只是感情好哩盲,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著狈醉,像睡著了一般廉油。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上苗傅,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天抒线,我揣著相機與錄音,去河邊找鬼渣慕。 笑死嘶炭,一個胖子當著我的面吹牛,可吹牛的內容都是我干的逊桦。 我是一名探鬼主播眨猎,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼强经!你這毒婦竟也來了睡陪?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎兰迫,沒想到半個月后信殊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡逮矛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年鸡号,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片须鼎。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡鲸伴,死狀恐怖,靈堂內的尸體忽然破棺而出晋控,到底是詐尸還是另有隱情汞窗,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布赡译,位于F島的核電站仲吏,受9級特大地震影響,放射性物質發(fā)生泄漏蝌焚。R本人自食惡果不足惜裹唆,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望只洒。 院中可真熱鬧许帐,春花似錦、人聲如沸毕谴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涝开。三九已至循帐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間舀武,已是汗流浹背拄养。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留银舱,地道東北人衷旅。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像纵朋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子茄袖,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內容