方法汇总
1、属性
2、方法
3、Delegate代理
4、Block回调
5、通知
6、KVO
7、单例
8、NSUserDefault(等本地化手段)
几种方式有着各自应用的场景,各有各的优点和缺点。
一、属性传值
使用场景:AtoB
说明:这种方式通常需要B对象,在A中设置B的属性,达到传值的目的。
使用:
在B中声明属性:
@property (copy ,nonatomic) NSString *value;
在A中修改B的属性
BCtrl *ctrl = [[BCtrl alloc] initWithNibName:@"BCtrl" bundle:nil];
ctrl.value = @"LOLITA";
[self.navigationController pushViewController:ctrl animated:YES];
这样在B中就可以使用这个value了。
二、方法传值
使用场景:AtoB
说明:这种方式也需要B对象,在A中调用B的方法并将数据传递给B
使用:
在B中声明方法
-(void)passValue:(NSDictionary *)dic;
在A中调用B的方法
[B_Object passValue:@{@"name":@"LOLITA"}];
B中使用传递过来的数据
-(void)passValue:(NSDictionary *)dic{
NSLog(@"%@",dic);
}
效果:
使用场景:BtoA
说明:思路跟AtoB一样,使用A对象调用其方法传递数据
首先在A中定义传值方法并实现该方法
.h 文件中 声明该方法
@interface ACtrl : UIViewController
-(void)passValue:(NSDictionary *)dic; // 传值方法
@end
.m 文件中 实现该方法
-(void)passValue:(NSDictionary *)dic{ // 使用传递过来的字典
NSLog(@"%@",dic);
}
在B中声明一个id类型属性
@property (weak ,nonatomic) id delegate; // 用来获得A对象,注意不要引起循环引用,我这里使用了weak
在A中初始化B的时候将A对象传递给B的这个属性
BCtrl *ctrl = [[BCtrl alloc] initWithNibName:@"BCtrl" bundle:nil];
ctrl.delegate = self; // 指向id属性
[self.navigationController pushViewController:ctrl animated:YES];
在B的某个特定事件下(如返回),调用A的传值方法将数据传递过去
- (IBAction)goBackBtn:(UIButton *)sender { // 返回事件
if (self.delegate&&[self.delegate respondsToSelector:@selector(passValue:)]) {
[self.delegate passValue:@{@"name":@"LOLITA",@"age":@"24"}]; //传递数据
}
[self.navigationController popViewControllerAnimated:YES];
}
这时候,A中的传值方法被调用,输出了B传过来的数据
在上面这个例子中,有人发现这样的使用和delegate很像,没错,delegate本来就是某个对象委托去达到某个目的而产生的设计模式,我这里使用A的传值方法来完成传值功能,那么我们iOS的设计模式中,完全可以将部分业务功能抽离出来,赋予代理,让其去实现这种功能。
三、Delegate代理传值
使用场景:BtoA
说明:代理模式是我们常用的设计模式,常用来委托完成某些业务功能,我们这里仅用来传递数据
首先我们新建一个协议文件,赋予其某种功能
#import
@protocol PageDelegateProtocol
// 这里没有实现,只有方法声明,有些类似java中的接口
-(void)passValue:(NSDictionary *)dic; // 具有传值功能的方法
@end
在B中导入该协议,并新建代理对象作为其属性
#import "PageDelegateProtocol.h"
@interface BCtrl : UIViewController
@property (nonatomic,weak) id delegate; // 代理对象
@end
在B的某个特定事件下(如返回),将数据传递给A
- (IBAction)goBackBtn:(UIButton *)sender {
if (self.delegate&&[self.delegate respondsToSelector:@selector(passValue:)]) {
//使用协议中的传值方法
[self.delegate passValue:@{@"name":@"LOLITA",@"age":@"24"}];
}
[self.navigationController popViewControllerAnimated:YES];
}
在A中,将A对象指向B中的代理
BCtrl *ctrl = [[BCtrl alloc] initWithNibName:@"BCtrl" bundle:nil];
ctrl.delegate = self;
[self.navigationController pushViewController:ctrl animated:YES];
A中遵守协议,并实现协议方法
-(void)passValue:(NSDictionary *)dic{
NSLog(@"%@",dic);
}
这样我们就使用了代理实现了页面的传值,我们可以通过和BtoA的方法传值方式的对比来理解代理的方式
四、Block回调传值
block可以作为属性传值、也可以作为参数传值
1、block作为属性来传值
使用场景:BtoA
使用:
首先在B页面声明一个block作为属性
typedef void(^passValue)(NSDictionary *dic); // 声明一个block
@interface ACtrl : UIViewController
@property (nonatomic,copy) passValue passDic; // 作为B的属性
@end
在特定事件(如返回事件)使用block回调
- (IBAction)goBackBtn:(UIButton *)sender {
if (self.passDic) {
self.passDic(@{@"name":@"LOLITA",@"age":@"24",@"sex":@"male"}); // 回调数据
}
[self.navigationController popViewControllerAnimated:YES];
}
在A中,实现block回调函数
BCtrl *ctrl = [[BCtrl alloc] initWithNibName:@"BCtrl" bundle:nil];
ctrl.passDic = ^(NSDictionary *dic) { // 实现block
NSLog(@"%@",dic);
};
[self.navigationController pushViewController:ctrl animated:YES];
2、block作为参数来传值
使用场景:BtoA
说明:这种方式在请求类中比较常见,在请求回来数据之后使用block回调数据,这里使用两个页面简单介绍使用过程
先在B类中,定义一个block
typedef void(^passValue)(NSDictionary *dic);
将bock作为参数声明一个方法
-(void)passDic:(passValue)dicBlock;
实现该方法
-(void)passDic:(passValue)dicBlock{
if (dicBlock) {
// 使用block回调数据
dicBlock(@{@"name":@"LOLITA",@"age":@"24"});
}
}
在A类中,使用B类调用带block参数的方法
BCtrl *ctrl = [[BCtrl alloc] initWithNibName:@"BCtrl" bundle:nil];
[ctrl passDic:^(NSDictionary *dic) {
NSLog(@"--%@",dic);
}];
五、通知传值
使用场景:Ato多个页面
说明:通知属于观察者模式,属于一对多的传递消息,相比于代理和block的一对一,通知的开销较大,所有只有在需要将消息传递给多个页面的时候再使用通知,另外通知属于单例
使用:
在需要接收通知消息的页面添加注册
// 给self添加通知事件,当name和发送方相同的时候才能接受到消息
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getUserInfo:) name:@"passValue" object:nil];
// 处理消息的方法
-(void)getUserInfo:(id)userInfo{
NSLog(@"%@",userInfo);
}
传递消息的页面,特定的事件发送通知(如返回)
- (IBAction)goBackBtn:(UIButton *)sender {
[self.navigationController popViewControllerAnimated:YES];
[[NSNotificationCenter defaultCenter] postNotificationName:@"passValue" object:nil userInfo:@{@"name":@"LOLITA",@"sex":@"male"}];
}
是不是非常方便呢?还需要注意的是:1、注册通知一定要在发送通知的代码执行之前 2、通知需要手动移除
// 在添加监听的页面移除通知,这里移除了name为passValue的通知,也可以不区分name全部移除
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"passValue" object:nil];
}
六、KVO (Key-Value Observing)键值观察
KVO顾名思义键值对观察,它通过监听被观察者的某些属性值的变化而响应某些事件,也是一种消息传递。
为了演示,我给B页面设置一个属性,作为被观察的属性
@interface BCtrl : UIViewController
@property (assign ,nonatomic) NSInteger value;
@end
使用:
首先,被观察者需要为自己被监听的属性添加监听事件,B是被观察者
// A页面中
- (IBAction)btn:(UIButton *)sender {
BCtrl *ctrl = [[BCtrl alloc] initWithNibName:@"BCtrl" bundle:nil];
// self是A页面,它监听B页面的value属性 ,options有多种,这里获取新值
[ctrl addObserver:self forKeyPath:@"value" options:NSKeyValueObservingOptionNew context:@"changeValue"];
[self.navigationController pushViewController:ctrl animated:YES];
}
观察者A,需要实现observeValueForKeyPath:ofObject:change:context:方法,来获取监听属性的变化
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == @"changeValue") {
if ([keyPath isEqualToString:@"value"]) {
NSLog(@"%@",change);
// 记得要移除观察者
[object removeObserver:self forKeyPath:@"value"];
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
这样当被观察者B的value属性发生变化时,A就可以得到消息并获取相应的值
- (IBAction)goBackBtn:(UIButton *)sender {
[self.navigationController popViewControllerAnimated:YES];
self.value = 10;
}
效果
以上方法都是明确知道被传递的对象是谁,要做什么,属于一种消息的传递,是事件响应(属性传值可以通过重写setter方法来实现事件的响应),但是单例和NSUserDefault不同,它们不像代理明确需要做什么,也不像通知可以传递消息,仅仅被用来存储和获取数据,并且,它们不知道谁存储的数据,也不知道谁来调用,这些事情需要我们自己主动去做。
七、单例
单例即在app生命进程中仅被初始化一次,它的内存地址在初始化时就确定了,所以我们可以在A页面存储数据,B页面取数据,并且值是相同的
注意:当app生命进程结束后,单例的内存空间也被释放了,数据会消失掉
使用:
首先我们需要新建单例对象
.h 文件
#import
@interface DataManager : NSObject
@property (strong ,nonatomic) NSDictionary *dic; // 存储的数据
+(instancetype)shareDataManager; // 初始化方法
@end
.m 文件
+(instancetype)shareDataManager{
static dispatch_once_t onceToken;
static DataManager *manager;
dispatch_once(&onceToken, ^{
manager = [DataManager new];
});
return manager;
}
使用单例存数据,在需要的地方导入单例文件
// 存储一个字典数据
[DataManager shareDataManager].dic = @{@"name":@"LOLITA",@"iOS":@"2"};
在需要取数据的地方
NSLog(@"%@",[DataManager shareDataManager].dic);
效果
八、NSUserDefault等本地化手段
NSUserDefault是使用KVC手段访问、修改数据,并会在本地持久化
存储数据:通过不同的key来存数据
[[NSUserDefaults standardUserDefaults] setObject:@"LOLITA" forKey:@"name"];
[[NSUserDefaults standardUserDefaults] synchronize];
获取数据:key要相同
[[NSUserDefaults standardUserDefaults] objectForKey:@"name"]
删除数据:
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"name"];
单例和NSUserDefault操作数据比较
单例的存储的数据在app结束时就消失了,NSUserDefaults属于本地化存储,存储的数据只要app不删除,就一直存在,所以在使用这两种方式存取数据时要根据业务需求来。
九、总结
自己体会吧。。。啊哈哈哈