在 iOS 的数据交付(数据传递)方式中,常用的方式有:参数传递、Delegate、Notification、Block、KVO 和 Target-Action。下面逐一说明下这几种方式:
参数传递
这是最常用的一种方法了,就是在一个类中声明一个 public 的属性,提供给调用者,举个例子:
/* A.h */
@interface A : UIView;
@property (nonatomic, strong) UIButton *title;
@end
/* B.m */
A *a = [A alloc] init];
a.title = @"title";
这个就是最简单的参数传递的例子了,也是最常用的一种方式。
Delegate
在 iOS 中说到 delegate 自然就是想到 protocol,想想当初为了面试,还真不少背概念,其实也很好理解,举个比较形象的例子:
某神壕,想在深圳出售一套200方的房子(想想我干一辈子也买不起~~~),但神壕不想自己搞,然后交给了中介(delegate)去卖,但是神壕还是有一定要求的,比如最少多少钱、最好能达到多少钱等等(protocol),而买家则必须达到这种要求(实现 protocol方法),神壕才会卖,否则他就不干。
看完神壕卖房的例子,我想应该能大概理解 delegate 和 protocol 的关系了。
举例个实际应用:
/* 头文件 */
/* 定义一个协议 */
@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
实现方法:
/* 实现类中的方法 */
- (void)finshToSelectPhoto {
if ([self.pickerDelegate respondsToSelector:@selector(YHPhotoPickerViewController:selectedPhotos:)]){
// todo some thing
}
}
在这里将数据交付出去。先判断delegate是否存在,然后再判断是否实现的协议的相关内容(若不实现需要crash时可以在判断里加上断言),当delegate 和 protocol 都实现了,就可以进行数据的交付了。
调用方法:
@interface ViewController : UIViewController <YHPhotoPickerViewControllerDelegate>
#pragma mark - YHPhotoPickerViewController Delegate
- (void)YHPhotoPickerViewController:(YHSelectPhotoViewController *)PhotoPickerViewController selectedPhotos:(NSArray *)photos {
// handle data
}
@end
这里处理交付的数据,需要实现协议方法
Notification
Notification,这东西又爱又恨,一不小心就坑到不要不要的,这里先不分析为什么,先说实现。Notification(观察者模式的一种实现) 和 Delegage 都是设计模块的一种,但是两种设计模式的使用场景不太一样。
先看下观察者模式(图片来自大话设计模式):
这里就不详细说设计模式,我看了下大话设计模式这本书,还是说得比较形象,有兴趣的可以看下,但我还是比较喜欢黑书那本。
在 iOS 中,apple 已经封装好了 Notification,不需要我们实现,这里我们直接使用就可以了:
发送通知:
// object 就是交付的参数了,如果想传递多个参数时,可以使用集合来传递
[[NSNotificationCenter defaultCenter] postNotificationName:@"NotificationName" object:nil];
再看上面,新手很容易被坑到,object 并不是交付的参数,看另一个接口:
userInfo 才是要交付的参数,object 是用来做过滤通知的,_ 有没有坑过呢,问下自己吧。
再看添加监听通知的方法:
- (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文档已经写得很详细了,如果有对接口方法有疑问,还是多去看下官方的接口文档。
Block
Block 可以理解为 OC 的匿名函数,也可以理解为 OC 中的一种特殊变量,它可以在两个对象之间将任意的代码块当做参数进行传递。
举个例子:
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) {
}];
关键还是 在两个对象之间将任意的代码块当做参数进行传递 这句话的理解,上面的代码实行就是把
^(NSDictionary *retDict) {
// 代码块(你要处理的操作)
}
这些代码块当作参数传递到别一个对象中使用。在使用block的时候,初学的时候遇到过很懵逼的问题,就是什么时候使用weak,是否需要strong回来,看到唐巧大神的公众号有几篇文章解释得非常好,就是self 持有 block,block 又持有 self 时,就会引起循环引用,这个时候就需要使用:
__weak __typeof(self)weakSelf = self;
这种情况,为防止block调用self时,self被释放的情况,就要使用:
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (strongSelf) {
}
详细可以去看巧神的公众号。
KVO
KVO(Key - Value - Observer) 又是观察者模式的一种实现,简单点说就是键值监听者,指定的对象的属性被修改后,监听的对象就会收到通知。
举例:
// 1.注册观察者
[user addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:user.name];
// 2.回调方法
- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context {
}
// 3.移除观察者
[user removeObserver:self forKeyPath:@"name"];
}
在这里可以通过context的值进行数据交付。
Target-Action
Target-Action(目标-动作模式),看起来有点抽象化,简单点来说就是:当某事件发生时,我们会xxx(目标,也可以说对象)的xxx方法(动作或行动)。
最简的例子就是button的点击事件,这个就不单独举例了。
选择何种交付方式
其实对于选择何种交付方式,对我个人来说,这方面经验还是不足够,这里只介绍自己遇到的坑,有更好的建议的朋友望指点。
合理使用Notification
上面介绍过Notification时也说了,一不小心就坑得不要不要的,比如说你忘记把某个监听给移除了,这个就是很苦逼的事情,内心是很酸爽的。这个主要是编码不规范导致的。另一人主要原因还是Notification的影响面不可控制,没有办法确认处理地方法只有唯一,或者明确处理的地方,特别多人协助时就更加混乱。这里虽然说了Notification的使用很多不好的地方,但并不是强调Notification的不好,只有最合适的设计模式,没有好或者最坏的设计模式,所有能用的设计模式,都是前人的经验和总结,它们的存在都是经过了前人的检验的。比如网络状态的切换就很合适使用Notification。
少用block
其实能用block实现的东西都可以通过delegate来实现,但要区分怎么选择的话,那就是看回调的内容,如果回调要做的东西都是一致的就选择delegate;如果每次回调回来时要做的东西都不同,就选择block。
block除了上面介绍的时候说过会可能导致循环引用,我们可以weak引用来解决这个方法,但block还是有可能延长了对象的生命周期,而delegate就不会有这种问题,因为它本身就是弱的引用。这里为什么说尽量少用block,主要还是因为深受其害啊,目前公司的项目,不管是在网络层还是业务层,清一色的block,刚才接手原来的项目时,每次调试到一半,我去,block,又一个block,又一个block,这个时候我们根本不知道block里做了什么只能一个个跳进去查看,发现里面又有block,内心是各种草泥马的,一个方法里面有几个block,block里面还有block,这代码可读性真的感觉为0,一个方法有1,2百行的代码,混合着各个不同的任务,方法的单一性呢。。。
别一个问题,使用block的时候,回调的的代码和调用逻辑又放在一起了,很容易就出现那种一个方法几百行代码的情况,说好的方法单一性感觉又没有了。
总结
合理使用Notification,少用block。这里就只介绍这些了,其它的在后面的学习有所体会时再来补充了,如果有更好见解的朋友,望指出,大家多分享交流。