系列文集:爆棧熱門 iOS 問題。目錄在此。倉薯翻譯,歡迎指正:)
問題
我知道 delegate 機制是怎么回事,也知道怎么用 delegate。但是自己寫一個 delegate 需要注意什么呢?
答案
Jesse Rusak,723 贊
我把他答案的要點摘錄如下:
用 weak
保存 delegate 屬性在 ARC 里要用weak來防止循環(huán)引用,因為一個對象的 delegate 往往持有這個對象本身。(比如,viewController 經(jīng)常會當它里面子 view 的 delegate。)
要檢查 @optional 方法是否實現(xiàn)
聲明 protocol 一般是這樣:
@protocol UIWebViewDelegate <NSObject>
// Declaration for Methods that 'must' be implemented'
@optional
- (void)webViewDidStartLoad:(UIWebView *)webView;
// ... other methods here
@end
大部分 delegate 方法都是@optional的。@optional的方法在調(diào)用之前要用-respondsToSelector:檢查一下 delegate 對這個方法真正實現(xiàn)了沒有。比如在UIWebView里應該有類似如下的代碼:
if([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
[self.delegate webViewDidStartLoad:self];
}
命名
delegate 方法一般以依靠 delegate 的類名開頭,然后把依靠 delegate 的對象用第一個參數(shù)傳進去。方法名一般常用will-、should-、did-這些詞。比如,webViewDidStartLoad:(參數(shù)就是那個 webView)就要比loadStarted(沒參數(shù))好得多。
性能小優(yōu)化
為了不用每調(diào)一次方法之前都要用respondsToSelector:來檢查這個方法有沒有實現(xiàn),我們可以在設 delegate 的時候就檢查哪些方法實現(xiàn)了、哪些沒實現(xiàn),然后緩存下來。最簡潔的辦法就是用一個標志位來保存,如下:
@protocol SomethingDelegate <NSObject>
@optional
- (void)something:(id)something didFinishLoadingItem:(id)item;
- (void)something:(id)something didFailWithError:(NSError *)error;
@end
@interface Something : NSObject
@property (nonatomic, weak) id <SomethingDelegate> delegate;
@end
@implementation Something {
struct {
unsigned int didFinishLoadingItem:1;
unsigned int didFailWithError:1;
} delegateRespondsTo;
}
@synthesize delegate;
- (void)setDelegate:(id <JSSomethingDelegate>)aDelegate {
if (delegate != aDelegate) {
delegate = aDelegate;
delegateRespondsTo.didFinishLoadingItem = [delegate respondsToSelector:@selector(something:didFinishLoadingItem:)];
delegateRespondsTo.didFailWithError = [delegate respondsToSelector:@selector(something:didFailWithError:)];
}
}
@end
這樣,在要調(diào) delegate 方法的時候,我們就可以直接檢查delegateRespondsTo 結(jié)構(gòu)體的屬性,不用再一遍又一遍調(diào)-respondsToSelector:了。
簡寫的 delegate
在有 protocol 之前,一般是給 NSObject 加一個 category 來聲明 delegate 可以實現(xiàn)的方法。比如,現(xiàn)如今CALayer還有:
@interface NSObject(CALayerDelegate)
- (void)displayLayer:(CALayer *)layer;
// ... other methods here
@end
意思是告訴編譯器,任何對象都可以實現(xiàn)displayLayer:方法。
如果這樣寫的話,在調(diào)用方法之前也要同樣要用上面提到的-respondsToSelector:來檢查。只要讓 delegate 對象實現(xiàn)這個方法,然后把它保存在delegate屬性里就行了,不用寫 protocol 什么的。蘋果官方的庫里有不少這樣的東西,但是新寫的代碼最好還是用上面聲明 protocol 的正規(guī)寫法。因為這種簡寫方法會污染NSObject(會干擾代碼自動補全),并且也讓編譯器不易檢查出打錯字之類的 error。
系列文集:爆棧熱門 iOS 問題
譯者:@戴倉薯