转载学习资料汇总iOS进阶指南

关于 iOS 中数据交付

2017-02-07  本文已影响360人  YxxxHao

在 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 都是设计模块的一种,但是两种设计模式的使用场景不太一样。

先看下观察者模式(图片来自大话设计模式):

图片.png

这里就不详细说设计模式,我看了下大话设计模式这本书,还是说得比较形象,有兴趣的可以看下,但我还是比较喜欢黑书那本。

在 iOS 中,apple 已经封装好了 Notification,不需要我们实现,这里我们直接使用就可以了:

发送通知:

// object 就是交付的参数了,如果想传递多个参数时,可以使用集合来传递
[[NSNotificationCenter defaultCenter] postNotificationName:@"NotificationName" object:nil];

再看上面,新手很容易被坑到,object 并不是交付的参数,看另一个接口:

图片.png

userInfo 才是要交付的参数,object 是用来做过滤通知的,_ 有没有坑过呢,问下自己吧。

再看添加监听通知的方法:

- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSNotificationName)aName object:(id)anObject;

这里也有objct,别纠结,直接看苹果 api 文档就好了:

图片.png

接收通知:

[[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。这里就只介绍这些了,其它的在后面的学习有所体会时再来补充了,如果有更好见解的朋友,望指出,大家多分享交流。

上一篇 下一篇

猜你喜欢

热点阅读