在 iOS 的數(shù)據(jù)交付(數(shù)據(jù)傳遞)方式中畔濒,常用的方式有:參數(shù)傳遞、Delegate、Notification宠哄、Block毛嫉、KVO 和 Target-Action。下面逐一說明下這幾種方式:
參數(shù)傳遞
這是最常用的一種方法了暴区,就是在一個類中聲明一個 public 的屬性,提供給調(diào)用者房交,舉個例子:
/* A.h */
@interface A : UIView;
@property (nonatomic, strong) UIButton *title;
@end
/* B.m */
A *a = [A alloc] init];
a.title = @"title";
這個就是最簡單的參數(shù)傳遞的例子了候味,也是最常用的一種方式隔心。
Delegate
在 iOS 中說到 delegate 自然就是想到 protocol硬霍,想想當初為了面試,還真不少背概念粱玲,其實也很好理解耐床,舉個比較形象的例子:
某神壕撩轰,想在深圳出售一套200方的房子(想想我干一輩子也買不起~~~),但神壕不想自己搞偎箫,然后交給了中介(delegate)去賣淹办,但是神壕還是有一定要求的恶复,比如最少多少錢谤牡、最好能達到多少錢等等(protocol),而買家則必須達到這種要求(實現(xiàn) protocol方法)恐疲,神壕才會賣,否則他就不干碳蛋。
看完神壕賣房的例子肃弟,我想應該能大概理解 delegate 和 protocol 的關系了茸炒。
舉例個實際應用:
/* 頭文件 */
/* 定義一個協(xié)議 */
@protocol YHPhotoPickerViewControllerDelegate <NSObject>
- (void)YHPhotoPickerViewController:(YHSelectPhotoViewController *)PhotoPickerViewController selectedPhotos:(NSArray *)photos;
- (void)selectedPhotoBeyondLimit:(int)count currentView:(UIView *)view;
@end
@interface YHSelectPhotoViewController : UIViewController
@property(weak, nonatomic) id<YHPhotoPickerViewControllerDelegate> pickerDelegate;
@end
實現(xiàn)方法:
/* 實現(xiàn)類中的方法 */
- (void)finshToSelectPhoto {
if ([self.pickerDelegate respondsToSelector:@selector(YHPhotoPickerViewController:selectedPhotos:)]){
// todo some thing
}
}
在這里將數(shù)據(jù)交付出去壁公。先判斷delegate是否存在,然后再判斷是否實現(xiàn)的協(xié)議的相關內(nèi)容(若不實現(xiàn)需要crash時可以在判斷里加上斷言)比肄,當delegate 和 protocol 都實現(xiàn)了芳绩,就可以進行數(shù)據(jù)的交付了撞反。
調(diào)用方法:
@interface ViewController : UIViewController <YHPhotoPickerViewControllerDelegate>
#pragma mark - YHPhotoPickerViewController Delegate
- (void)YHPhotoPickerViewController:(YHSelectPhotoViewController *)PhotoPickerViewController selectedPhotos:(NSArray *)photos {
// handle data
}
@end
這里處理交付的數(shù)據(jù)遏片,需要實現(xiàn)協(xié)議方法
Notification
Notification吮便,這東西又愛又恨,一不小心就坑到不要不要的许师,這里先不分析為什么僚匆,先說實現(xiàn)。Notification(觀察者模式的一種實現(xiàn)) 和 Delegage 都是設計模塊的一種咧擂,但是兩種設計模式的使用場景不太一樣逞盆。
先看下觀察者模式(圖片來自大話設計模式):
這里就不詳細說設計模式,我看了下大話設計模式這本書屋确,還是說得比較形象,有興趣的可以看下,但我還是比較喜歡黑書那本攻臀。
在 iOS 中焕数,apple 已經(jīng)封裝好了 Notification,不需要我們實現(xiàn)刨啸,這里我們直接使用就可以了:
發(fā)送通知:
// object 就是交付的參數(shù)了堡赔,如果想傳遞多個參數(shù)時设联,可以使用集合來傳遞
[[NSNotificationCenter defaultCenter] postNotificationName:@"NotificationName" object:nil];
再看上面善已,新手很容易被坑到,object 并不是交付的參數(shù)离例,看另一個接口:
userInfo 才是要交付的參數(shù)换团,object 是用來做過濾通知的,_ 有沒有坑過呢宫蛆,問下自己吧艘包。
再看添加監(jiān)聽通知的方法:
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSNotificationName)aName object:(id)anObject;
這里也有objct,別糾結耀盗,直接看蘋果 api 文檔就好了:
接收通知:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sensorStateChange:) name:UIDeviceProximityStateDidChangeNotification object:nil];
- (void)sensorStateChange:(NSNotificationCenter *)notification { ... }
其實蘋果的api文檔已經(jīng)寫得很詳細了想虎,如果有對接口方法有疑問,還是多去看下官方的接口文檔叛拷。
Block
Block 可以理解為 OC 的匿名函數(shù)舌厨,也可以理解為 OC 中的一種特殊變量,它可以在兩個對象之間將任意的代碼塊當做參數(shù)進行傳遞忿薇。
舉個例子:
typedef void (^DictionaryResponseBlock)(NSDictionary *retDict);
typedef void (^errorBlock)(NSError *error);
+ (void)reqeustWeatherInfo:(NSString *)cityName
successCallback:(DictionaryResponseBlock)successCallback
failCallback:(errorBlock)failCallback;
[XXXXX reqeustWeatherInfo:@"cityName" successCallback:^(NSDictionary *retDict) {
// 代碼塊(你要處理的操作)
} failCallback:^(NSError *error) {
}];
關鍵還是 在兩個對象之間將任意的代碼塊當做參數(shù)進行傳遞 這句話的理解裙椭,上面的代碼實行就是把
^(NSDictionary *retDict) {
// 代碼塊(你要處理的操作)
}
這些代碼塊當作參數(shù)傳遞到別一個對象中使用。在使用block的時候煌恢,初學的時候遇到過很懵逼的問題骇陈,就是什么時候使用weak,是否需要strong回來瑰抵,看到唐巧大神的公眾號有幾篇文章解釋得非常好你雌,就是self 持有 block,block 又持有 self 時二汛,就會引起循環(huán)引用婿崭,這個時候就需要使用:
__weak __typeof(self)weakSelf = self;
這種情況,為防止block調(diào)用self時肴颊,self被釋放的情況氓栈,就要使用:
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (strongSelf) {
}
詳細可以去看巧神的公眾號。
KVO
KVO(Key - Value - Observer) 又是觀察者模式的一種實現(xiàn)婿着,簡單點說就是鍵值監(jiān)聽者授瘦,指定的對象的屬性被修改后醋界,監(jiān)聽的對象就會收到通知。
舉例:
// 1.注冊觀察者
[user addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:user.name];
// 2.回調(diào)方法
- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context {
}
// 3.移除觀察者
[user removeObserver:self forKeyPath:@"name"];
}
在這里可以通過context的值進行數(shù)據(jù)交付提完。
Target-Action
Target-Action(目標-動作模式)形纺,看起來有點抽象化,簡單點來說就是:當某事件發(fā)生時徒欣,我們會xxx(目標逐样,也可以說對象)的xxx方法(動作或行動)。
最簡的例子就是button的點擊事件打肝,這個就不單獨舉例了脂新。
選擇何種交付方式
其實對于選擇何種交付方式,對我個人來說粗梭,這方面經(jīng)驗還是不足夠争便,這里只介紹自己遇到的坑,有更好的建議的朋友望指點楼吃。
合理使用Notification
上面介紹過Notification時也說了始花,一不小心就坑得不要不要的,比如說你忘記把某個監(jiān)聽給移除了孩锡,這個就是很苦逼的事情酷宵,內(nèi)心是很酸爽的。這個主要是編碼不規(guī)范導致的躬窜。另一人主要原因還是Notification的影響面不可控制浇垦,沒有辦法確認處理地方法只有唯一,或者明確處理的地方荣挨,特別多人協(xié)助時就更加混亂男韧。這里雖然說了Notification的使用很多不好的地方,但并不是強調(diào)Notification的不好默垄,只有最合適的設計模式此虑,沒有好或者最壞的設計模式,所有能用的設計模式口锭,都是前人的經(jīng)驗和總結朦前,它們的存在都是經(jīng)過了前人的檢驗的。比如網(wǎng)絡狀態(tài)的切換就很合適使用Notification鹃操。
少用block
其實能用block實現(xiàn)的東西都可以通過delegate來實現(xiàn)韭寸,但要區(qū)分怎么選擇的話,那就是看回調(diào)的內(nèi)容荆隘,如果回調(diào)要做的東西都是一致的就選擇delegate恩伺;如果每次回調(diào)回來時要做的東西都不同,就選擇block椰拒。
block除了上面介紹的時候說過會可能導致循環(huán)引用晶渠,我們可以weak引用來解決這個方法凰荚,但block還是有可能延長了對象的生命周期,而delegate就不會有這種問題褒脯,因為它本身就是弱的引用浇揩。這里為什么說盡量少用block,主要還是因為深受其害啊憨颠,目前公司的項目,不管是在網(wǎng)絡層還是業(yè)務層积锅,清一色的block爽彤,剛才接手原來的項目時,每次調(diào)試到一半缚陷,我去适篙,block,又一個block箫爷,又一個block嚷节,這個時候我們根本不知道block里做了什么只能一個個跳進去查看,發(fā)現(xiàn)里面又有block虎锚,內(nèi)心是各種草泥馬的硫痰,一個方法里面有幾個block,block里面還有block窜护,這代碼可讀性真的感覺為0效斑,一個方法有1,2百行的代碼柱徙,混合著各個不同的任務缓屠,方法的單一性呢。护侮。敌完。
別一個問題,使用block的時候羊初,回調(diào)的的代碼和調(diào)用邏輯又放在一起了滨溉,很容易就出現(xiàn)那種一個方法幾百行代碼的情況,說好的方法單一性感覺又沒有了凳忙。
總結
合理使用Notification业踏,少用block。這里就只介紹這些了涧卵,其它的在后面的學習有所體會時再來補充了勤家,如果有更好見解的朋友,望指出柳恐,大家多分享交流伐脖。