6.屬性
將屬性聲明為@dynamic颂斜,編譯器則不會為其自動生成實例變量及存取方法(setter夫壁、getter方法);
@implementation SomeClass
@dynamic productId,productName;
- 可以用@property語法來定義對象中所封裝的數(shù)據(jù)沃疮;
- 通過“修飾詞”來指定存儲數(shù)據(jù)所需的正確語義盒让;
- 在設(shè)置屬性所對應(yīng)的實例變量時,一定要遵從該屬性所聲明的語義司蔬;
- 開發(fā)iOS程序時應(yīng)該使用nonatomic屬性邑茄,因為atomic(同步鎖)屬性嚴(yán)重影響性能。
7.在對象內(nèi)部盡量直接訪問實例變量
在對象內(nèi)部訪問實例變量時葱她,是通過屬性(self.proper)來訪問還是通過_proper來訪問區(qū)別在于是否執(zhí)行屬性的setter撩扒、getting方法。
要點:
- 在對象內(nèi)部讀取數(shù)據(jù)時吨些,應(yīng)該直接通過實例變量來讀搓谆,而寫入數(shù)據(jù)時,則應(yīng)通過屬性來寫豪墅;
- 在初始化方法及dealloc方法中泉手,總是應(yīng)該直接通過實例變量來讀寫數(shù)據(jù)。
- 有時會使用惰性初始化技術(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)其“指針值”完全相等時内狸,這兩個對象才相等。
要點:
- 要想檢測對象的等同性厘擂,請?zhí)峁癷sEqual:”與hash方法昆淡;
- 相同的對象必須具有相同的哈希碼,但是兩個哈希碼相同的對象未必相同刽严;
- 不要盲目地逐個檢測每條屬性昂灵,而是應(yīng)該依照具體需求來定制檢測方案;
- 編寫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;
要點:
- 類族模式可以把實現(xiàn)細(xì)節(jié)隱藏在一套簡單的公共接口后面;
- 系統(tǒng)框架中經(jīng)常使用到類族艇炎;
- 從類族的公共抽象基類中繼承子類時要當(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)
要點:
- 可以通過“關(guān)聯(lián)對象”機(jī)制來把兩個對象連起來居砖;
- 定義關(guān)聯(lián)對象時可指定內(nèi)存管理語義,用以模仿定義屬性時采用的擁有關(guān)系與非擁有關(guān)系驴娃;
- 只有在其他做法不可行時才應(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)操作急迂。
要點:
- 消息由接受者袋毙、選擇子及參數(shù)構(gòu)成皆看。給某對象“發(fā)送消息”(invoke a message)也就相當(dāng)于在該對象上“調(diào)用方法”(call a method)。
- 發(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í)行過程:
- 對象發(fā)送一條無法解讀的消息,也就是調(diào)用一個沒有實現(xiàn)方法侦镇;
- 首先灵疮,征詢接受者所屬的類,看其是否能動態(tài)添加方法壳繁,以處理這個“未知的方法”震捣,這叫做動態(tài)方法解析荔棉。就是上面的resolveClassMethod:與resolveInstanceMethod:方法
- 倘若沒有動態(tài)新增方法來響應(yīng)該選擇子,則該對象會檢查是否有其他對象來處理這條消息蒿赢,也就是執(zhí)行forwardingTargetForSelector:方法尋找備授接受者
- 若forwardingTargetForSelector返回nil,沒有其他對象處理該消息润樱,則運行期系統(tǒng)會啟動完整的消息轉(zhuǎn)發(fā)機(jī)制,運行期系統(tǒng)會把與消息有關(guān)的全部細(xì)節(jié)都封裝到NSInvocation對象中羡棵,再給接受者最后一次機(jī)會壹若,令其設(shè)法解決當(dāng)前還未處理的這條消息。
- 若最終仍無法處理該消息晾腔,那么會調(diào)用NSObject的- (void)doesNotRecognizeSelector:(SEL)aSelector方法舌稀,拋出異常啊犬。
模擬消息發(fā)送無法找到相應(yīng)方法的步驟:
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:的方法
要點:
- 在運行期,可以向類中新增或替換選擇子所對應(yīng)的方法實現(xiàn)应闯;
- 使用另一份實現(xiàn)來替換原有的方法實現(xiàn)纤控,這道工序叫做“方法調(diào)配”,開發(fā)者常用此技術(shù)向原有實現(xiàn)中添加新功能碉纺;
- 一般來說船万,只有調(diào)試程序的時候才需要在運行期修改方法實現(xiàn),這種做法不宜濫用骨田。
14.理解“類對象”的用意
要點:
- 每個實例都有一個指向Class對象的指針耿导,用以表明其類型,而這些Class對象則構(gòu)成了類的繼承體系态贤;
- 如果對象類型無法在編譯期確定舱呻,那么就應(yīng)該使用類型信息查詢方法來探知;
- 盡量使用類型信息查詢方法來確定對象類型悠汽,而不要直接比較類對象箱吕,因為某些對象可能實現(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)框架