OC中兩項重要的語言特性:
-
協議:與Java中的“接口”類似,但不支持多重繼承,最常見的用途是實現委托模式。
多重繼承指的是一個類可以同時繼承多個類,比如A類繼承自B類和C類,這就是多重繼承。
分類:利用分類機制,我們無須繼承子類即可直接為當前類添加方法。因為OC運行期系統是高度動態的,但也隱藏著一些陷阱。
23. 通過委托與數據源協議進行對象間通信
@property (nonatomic, weak) id <xxxxDelegate> delegate;
如果是方法是可選的(optional),在委托類中,需要先執行判斷(responseToSelector)操作。
if ([_delegate responseToSelector:@selector(xxxxx)]) {
[_delegate xxxxx];
}
完整的例子:
//
// EOCNetworkFetcher.h
//
#import <Foundation/Foundation.h>
@class EOCNetworkFetcher;
@protocol EOCNetworkFetcherDelegate <NSObject>
@optional
- (void)networkFetcher:(EOCNetworkFetcher *)fetcher didReceiveData:(NSData *)data;
- (void)networkFetcher:(EOCNetworkFetcher *)fetcher didFailWithError:(NSError *)error;
- (void)networkFetcher:(EOCNetworkFetcher *)fetcher didUpdateProgressTo:(float)progress;
@end
@interface EOCNetworkFetcher : NSObject
@property (nonatomic, weak) id <EOCNetworkFetcherDelegate> delegate;
- (void)requestWithParams:(id)params url:(NSURL *)url;
@end
/*******************************************/
/*******************************************/
//
// EOCNetworkFetcher.m
//
#import "EOCNetworkFetcher.h"
@interface EOCNetworkFetcher (){
/**
這個結構體,用來緩存委托對象是否能響應特定的選擇子
2^1 = 2,表示0 和 1 兩個數。
*/
struct {
unsigned int didReceiveData: 1;
unsigned int didFailWithError: 1;
unsigned int didUpdateProgressTo: 1;
} _delegateFlags;
}
@end
@implementation EOCNetworkFetcher {}
- (void)setDelegate:(id<EOCNetworkFetcherDelegate>)delegate {
_delegate = delegate;
/* 優化代碼執行速度
這樣就不用每次在實現要被代理的方法前,每次執行檢查了。只要檢查一次就好。因為在設置代理時就已經確定了。
*/
_delegateFlags.didReceiveData = [delegate respondsToSelector:@selector(networkFetcher:didReceiveData:)];
_delegateFlags.didFailWithError = [delegate respondsToSelector:@selector(networkFetcher:didFailWithError:)];
_delegateFlags.didUpdateProgressTo = [delegate respondsToSelector:@selector(networkFetcher:didUpdateProgressTo:)];
}
- (void)requestWithParams:(id)params url:(NSURL *)url {
if (_delegateFlags.didReceiveData) {
[_delegate networkFetcher:self didReceiveData:[NSData new]];
}
if (_delegateFlags.didFailWithError) {
[_delegate networkFetcher:self didFailWithError:[NSError new]];
}
if (_delegateFlags.didUpdateProgressTo) {
[_delegate networkFetcher:self didUpdateProgressTo:1.0f];
}
}
@end
要點
- 委托模式為對象提供了一套接口,使其可由此將相關事件告知其他對象。
- 將委托的對象應該支持的接口定義成協議,在協議中將可能需要的事件定義成方法。
- 當某一對象需要從另一對象獲取數據時,可以使用委托模式。在這種情況下,該協議被稱為 “數據源協議”。
- 若有必要,可實現含有位段的結構體,將委托對象是否響應相關代理方法 這一信息緩存至其中。
在結構體中,
fileA 位段將占用8個二進制位,0~255;
fileB 位段將占用4個二進制位,0~16;
fileC 位段將占用2個二進制位,0~4;
fileD 位段將占用1個二進制位,0~1;
struct file {
unsigned int fileA: 8;
unsigned int fileB: 4;
unsigned int fileC: 2;
unsigned int fileD: 1;
} ;
24. 將類的實現代碼分散到便于管理的數個分類之中
要點
- 使用 分類機制 把類的實現代碼劃分為易于管理的小塊。
- 將應該視為 “私有” 的方法歸入名為 “private” 的分類中,用以隱藏實現細節。
將類代碼打散到分類中,便于調試
- 調試會出現分類名稱,如-[EOCPerson(FriendShip) addFriend:]
- 方便查看,可以給類進行“瘦身”。
25. 總是為第三方類的分類名稱加上前綴
@interface NSString (WZZ_HTTP)
- (NSString *)wzz_doSomeThing1;
- (NSString *)wzz_doSomeThing2;
@end
要點總結
- 向第三方類中添加分類時,總應給其 名稱 和其中的 方法 加上你專用的前綴。----避免多次覆蓋
26. 勿在分類中聲明屬性
屬性,是用來封裝數據的。
要點
- 把封裝數據所用的全部屬性都定義在 主接口 里;
- 在 “class-continuation 分類” 之外的所有分類里,可以定義存取 方法 ,但不應聲明 屬性 。
27. 使用 “class-continuation” 分類實現隱藏細節
@interface EOCPerson ()
@end
//或
@implementation EOCPerson
@end
要點
- 通過 “class—continuation分類” ,向類中增加實例變量。
- 如果屬性在主接口聲明中為“只讀” ,而類的內容又要用設置方法修改此屬性,那么就在 “class-continuation分類” 中將其擴展為可讀寫。
//.h 中
@property (nonatomic, copy, readOnly) NSString *name;
//.m 中
@property (nonatomic, copy, readWrite) NSString *name;
- 把私有方法的原型聲明在 “class-continuation分類” 里面。
//.m
@interface EOCPerson ()
- (void)p_privateMethod;//私有方法
@end
- 若想隱藏此類所實現的協議,則可在該類的 “class-continuation分類” 中聲明。
//.m
@interface EOCPerson() <EOCSecretDelegate>
@end
28. 通過協議提供匿名對象
要點
- (id<EOCSecretProtocol>)doSomeThing;
- 協議在某種程度上可以提供匿名類型。具體的類型可以淡化成遵從某協議的id類型,協議里規定了對象所應實現的代理方法。
- 使用匿名對象來隱藏類型名稱(或類名)。
- 如果類型不重要,重要的是對象能夠響應(某個協議的)特定方法,那么可以使用你匿名對象來表示。