編寫高質(zhì)量代碼的52個有效方法(二)—對象嗽交、消息、運行期

image

6.屬性

將屬性聲明為@dynamic颂斜,編譯器則不會為其自動生成實例變量及存取方法(setter夫壁、getter方法);

@implementation SomeClass

@dynamic productId,productName;

  1. 可以用@property語法來定義對象中所封裝的數(shù)據(jù)沃疮;
  2. 通過“修飾詞”來指定存儲數(shù)據(jù)所需的正確語義盒让;
  3. 在設(shè)置屬性所對應(yīng)的實例變量時,一定要遵從該屬性所聲明的語義司蔬;
  4. 開發(fā)iOS程序時應(yīng)該使用nonatomic屬性邑茄,因為atomic(同步鎖)屬性嚴(yán)重影響性能。

7.在對象內(nèi)部盡量直接訪問實例變量

在對象內(nèi)部訪問實例變量時葱她,是通過屬性(self.proper)來訪問還是通過_proper來訪問區(qū)別在于是否執(zhí)行屬性的setter撩扒、getting方法。

要點:

  1. 在對象內(nèi)部讀取數(shù)據(jù)時吨些,應(yīng)該直接通過實例變量來讀搓谆,而寫入數(shù)據(jù)時,則應(yīng)通過屬性來寫豪墅;
  2. 在初始化方法及dealloc方法中泉手,總是應(yīng)該直接通過實例變量來讀寫數(shù)據(jù)。
  3. 有時會使用惰性初始化技術(shù)配置某份數(shù)據(jù)偶器,這種情況下斩萌,需要通過屬性來讀取數(shù)據(jù)。

8.理解“對象等同性”這一概念

“等同性”(equality)在開發(fā)中時常作為邏輯判斷的依據(jù)屏轰。按照 “==”操作符比較颊郎,對于常規(guī)的數(shù)據(jù)類型比較是值,比如 9 == 9 ;對于對象的比較霎苗,使用 == 則比較的是兩個指針本身(可理解為內(nèi)存地址)姆吭。對于系統(tǒng)框架中的對象相等 比較,我們可以使用NSObject協(xié)議中聲明的“isEqual:”方法來判斷兩個對象的等同性唁盏。

//NSObject協(xié)議中有兩個用于判斷等同性的關(guān)鍵方法
- (BOOL)isEqual:(id)object;
- (NSUInteger)hash;

NSObject類對這兩個方法的默認(rèn)實現(xiàn)是:當(dāng)且僅當(dāng)其“指針值”完全相等時内狸,這兩個對象才相等。

要點:

  1. 要想檢測對象的等同性厘擂,請?zhí)峁癷sEqual:”與hash方法昆淡;
  2. 相同的對象必須具有相同的哈希碼,但是兩個哈希碼相同的對象未必相同刽严;
  3. 不要盲目地逐個檢測每條屬性昂灵,而是應(yīng)該依照具體需求來定制檢測方案;
  4. 編寫hash方法時,應(yīng)該使用計算速度快而且哈希碼碰撞幾率低的算法倔既。

9.以“類族模式”隱藏實現(xiàn)細(xì)節(jié)

做過Java開發(fā)的同學(xué)應(yīng)該知道恕曲,被abstract修飾的類是抽象類,而Objective-C中也有抽象類鹏氧,比如CAAnaimation渤涌、NSOperation等,我們只是使用其子類把还,而不能直接使用抽象類实蓬。類族模式 就是定義一個基類,多個不同特性與功能的子類繼承自它吊履,基類提供一個初始化類的方法以及相關(guān)功能方法安皱,子類重寫父類的方法.

//確定一個對象是否是該類的實例,或者是該類子類的實例
- (BOOL)isKindOfClass:(Class)aClass;

//確定一個對象是否是當(dāng)前類的實例.
- (BOOL)isMemberOfClass:(Class)aClass;

要點:

  1. 類族模式可以把實現(xiàn)細(xì)節(jié)隱藏在一套簡單的公共接口后面;
  2. 系統(tǒng)框架中經(jīng)常使用到類族艇炎;
  3. 從類族的公共抽象基類中繼承子類時要當(dāng)心酌伊,若有開發(fā)文檔,則應(yīng)首先閱讀缀踪。

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

//此方法以給定的鍵和策略為某對象設(shè)置關(guān)聯(lián)對象值
void objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
                         id _Nullable value, objc_AssociationPolicy policy)
                         
//此方法根據(jù)給定的鍵從某對象中獲取相應(yīng)的關(guān)聯(lián)對象值 
id _Nullable objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)

//此方法移除指定對象的全部關(guān)聯(lián)對象
void objc_removeAssociatedObjects(id _Nonnull object)

要點:

  1. 可以通過“關(guān)聯(lián)對象”機(jī)制來把兩個對象連起來居砖;
  2. 定義關(guān)聯(lián)對象時可指定內(nèi)存管理語義,用以模仿定義屬性時采用的擁有關(guān)系與非擁有關(guān)系驴娃;
  3. 只有在其他做法不可行時才應(yīng)選用關(guān)聯(lián)對象奏候,因為這種做法通常會引入難于查找的bug。

11.理解objc_msgSend(消息發(fā)送)的作用

開發(fā)中時常會遇到 unrecognized selector sent to instance 0x87 ... 的問題唇敞,對分類的方法無法找到蔗草,我們知道配置一下 -ObjC就可以解決。該問題的本質(zhì)原因就是方法調(diào)用也就是消息發(fā)送過程中無法找到對應(yīng)的方法疆柔。

//方法調(diào)用實際上就是消息發(fā)送
objc_msgSend(id obj, SEL cmd,...)

eg:
id returnValue = [someObject messageName:parameter];
//someObject叫做“接受者”(receiver),messageName叫做“選擇子”(selector)咒精。選擇子與參數(shù)合起來稱為“消息”(message).編譯器看到此消息后,將其轉(zhuǎn)換為一條標(biāo)準(zhǔn)的C語言函數(shù)調(diào)用旷档,所調(diào)用的函數(shù)乃是消息傳遞機(jī)制中的核心函數(shù)模叙,就是objc_msgSend,編譯器把上面的方法調(diào)用會轉(zhuǎn)換為如下函數(shù)

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

方法調(diào)用的過程:objc_msgSend函數(shù)會依據(jù)接受者與選擇子的類型來調(diào)用適當(dāng)?shù)姆椒ū蚍浮榱送瓿纱瞬僮飨蚵ィ摲椒ㄐ枰诮邮苷咚鶎俚念愔兴褜て洹胺椒斜怼?list of methods),如果能找到與選擇子名稱相符的方法,就跳至其實現(xiàn)代碼湖蜕;若是找不到,就沿著繼承體系繼續(xù)向上查找宋列,等找到合適的方法之后再跳轉(zhuǎn)昭抒。如果最終還是找不到相符的方法罚缕,那就執(zhí)行“消息轉(zhuǎn)發(fā)”(mesageforwarding)操作急迂。

要點:

  1. 消息由接受者袋毙、選擇子及參數(shù)構(gòu)成皆看。給某對象“發(fā)送消息”(invoke a message)也就相當(dāng)于在該對象上“調(diào)用方法”(call a method)。
  2. 發(fā)給對象的全部消息都要由“動態(tài)消息派發(fā)系統(tǒng)”來處理背零,該系統(tǒng)會查出對應(yīng)的方法腰吟,并執(zhí)行其代碼。

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

首先理解NSObject的方法:

//接受到無法解讀的 類方法消息 時調(diào)用
+ (BOOL)resolveClassMethod:(SEL)sel ;


//接受到無法解讀的 實例方法的消息 時調(diào)用
+ (BOOL)resolveInstanceMethod:(SEL)sel ;

//備授接受者
- (id)forwardingTargetForSelector:(SEL)aSelector徙瓶;

//轉(zhuǎn)發(fā)消息
- (void)forwardInvocation:(NSInvocation *)anInvocation;
//方法簽名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector毛雇;

//無法找到方法時調(diào)用
- (void)doesNotRecognizeSelector:(SEL)aSelector;

對于一個方法調(diào)用無法找到相應(yīng)方法時運行期系統(tǒng)的執(zhí)行過程:

  1. 對象發(fā)送一條無法解讀的消息,也就是調(diào)用一個沒有實現(xiàn)方法侦镇;
  2. 首先灵疮,征詢接受者所屬的類,看其是否能動態(tài)添加方法壳繁,以處理這個“未知的方法”震捣,這叫做動態(tài)方法解析荔棉。就是上面的resolveClassMethod:與resolveInstanceMethod:方法
  3. 倘若沒有動態(tài)新增方法來響應(yīng)該選擇子,則該對象會檢查是否有其他對象來處理這條消息蒿赢,也就是執(zhí)行forwardingTargetForSelector:方法尋找備授接受者
  4. 若forwardingTargetForSelector返回nil,沒有其他對象處理該消息润樱,則運行期系統(tǒng)會啟動完整的消息轉(zhuǎn)發(fā)機(jī)制,運行期系統(tǒng)會把與消息有關(guān)的全部細(xì)節(jié)都封裝到NSInvocation對象中羡棵,再給接受者最后一次機(jī)會壹若,令其設(shè)法解決當(dāng)前還未處理的這條消息。
  5. 若最終仍無法處理該消息晾腔,那么會調(diào)用NSObject的- (void)doesNotRecognizeSelector:(SEL)aSelector方法舌稀,拋出異常啊犬。

模擬消息發(fā)送無法找到相應(yīng)方法的步驟:


image

13.用“方法調(diào)配技術(shù)”調(diào)試“黑盒方法”

方法調(diào)配技術(shù)也就是方法交換技術(shù)灼擂。用到的運行時方法是

//獲取類的實例方法 返回一個Method對象
class_getInstanceMethod(Class obj, SEL cmd)

//替換方法的實現(xiàn)
method_exchangeImplementations(method1, method2)

方法交換常常用于對系統(tǒng)方法的補(bǔ)充,比如:我們要打印每次調(diào)用imageWithNamed:方法的時間觉至,則我們可以自定義一個方法剔应,在該方法中進(jìn)行圖片讀取,同時再打印出當(dāng)前時間语御,然后把自定義的方法與UIImage的該方法進(jìn)行交換峻贮,這樣我們就不必修改項目中每一處使用UIImageNamed:的方法

運行時機(jī)制runtime(交換方法)

要點:

  1. 在運行期,可以向類中新增或替換選擇子所對應(yīng)的方法實現(xiàn)应闯;
  2. 使用另一份實現(xiàn)來替換原有的方法實現(xiàn)纤控,這道工序叫做“方法調(diào)配”,開發(fā)者常用此技術(shù)向原有實現(xiàn)中添加新功能碉纺;
  3. 一般來說船万,只有調(diào)試程序的時候才需要在運行期修改方法實現(xiàn),這種做法不宜濫用骨田。

14.理解“類對象”的用意

要點:

  1. 每個實例都有一個指向Class對象的指針耿导,用以表明其類型,而這些Class對象則構(gòu)成了類的繼承體系态贤;
  2. 如果對象類型無法在編譯期確定舱呻,那么就應(yīng)該使用類型信息查詢方法來探知;
  3. 盡量使用類型信息查詢方法來確定對象類型悠汽,而不要直接比較類對象箱吕,因為某些對象可能實現(xiàn)了消息轉(zhuǎn)發(fā)功能。



PDF格式的資料來自iOS開發(fā)交流群柿冲、感覺作者的貢獻(xiàn)茬高,對于知識的系統(tǒng)歸納總結(jié)很有幫助。
編寫高質(zhì)量代碼的52個有效方法
編寫高質(zhì)量代碼的52個有效方法(一)—熟悉OC
編寫高質(zhì)量代碼的52個有效方法(二)—對象姻采、消息雅采、運行期
編寫高質(zhì)量代碼的52個有效方法(三)—接口與API設(shè)計
編寫高質(zhì)量代碼的52個有效方法(四)—協(xié)議與分類
編寫高質(zhì)量代碼的52個有效方法(五)—內(nèi)存管理
編寫高質(zhì)量代碼的52個有效方法(六)—塊與大中樞派發(fā)
編寫高質(zhì)量代碼的52個有效方法(七)---系統(tǒng)框架

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末爵憎,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子婚瓜,更是在濱河造成了極大的恐慌宝鼓,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件巴刻,死亡現(xiàn)場離奇詭異愚铡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)胡陪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進(jìn)店門沥寥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人柠座,你說我怎么就攤上這事邑雅。” “怎么了妈经?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵淮野,是天一觀的道長。 經(jīng)常有香客問我吹泡,道長骤星,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任爆哑,我火速辦了婚禮洞难,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘揭朝。我一直安慰自己队贱,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布萝勤。 她就那樣靜靜地躺著露筒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪敌卓。 梳的紋絲不亂的頭發(fā)上慎式,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天,我揣著相機(jī)與錄音趟径,去河邊找鬼瘪吏。 笑死,一個胖子當(dāng)著我的面吹牛蜗巧,可吹牛的內(nèi)容都是我干的掌眠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼幕屹,長吁一口氣:“原來是場噩夢啊……” “哼蓝丙!你這毒婦竟也來了级遭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤渺尘,失蹤者是張志新(化名)和其女友劉穎挫鸽,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鸥跟,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡丢郊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了医咨。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枫匾。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖拟淮,靈堂內(nèi)的尸體忽然破棺而出干茉,到底是詐尸還是另有隱情,我是刑警寧澤惩歉,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布等脂,位于F島的核電站,受9級特大地震影響撑蚌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜搏屑,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一争涌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辣恋,春花似錦亮垫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至携狭,卻和暖如春继蜡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背逛腿。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工稀并, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人单默。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓碘举,卻偏偏與公主長得像,于是被迫代替她去往敵國和親搁廓。 傳聞我的和親對象是個殘疾皇子引颈,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,652評論 2 354

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