方法汇总

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);
}

效果:

body传值 ios ios传值方式_body传值 ios


使用场景: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传过来的数据

body传值 ios ios传值方式_页面传值_02

在上面这个例子中,有人发现这样的使用和delegate很像,没错,delegate本来就是某个对象委托去达到某个目的而产生的设计模式,我这里使用A的传值方法来完成传值功能,那么我们iOS的设计模式中,完全可以将部分业务功能抽离出来,赋予代理,让其去实现这种功能。


三、Delegate代理传值

使用场景:BtoA

说明:代理模式是我们常用的设计模式,常用来委托完成某些业务功能,我们这里仅用来传递数据

首先我们新建一个协议文件,赋予其某种功能

#import <Foundation/Foundation.h>
@protocol PageDelegateProtocol <NSObject>
// 这里没有实现,只有方法声明,有些类似java中的接口
-(void)passValue:(NSDictionary *)dic;   // 具有传值功能的方法
@end

在B中导入该协议,并新建代理对象作为其属性

#import "PageDelegateProtocol.h"
@interface BCtrl : UIViewController
@property (nonatomic,weak) id <PageDelegateProtocol> 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;
}

效果

body传值 ios ios传值方式_页面传值_03



以上方法都是明确知道被传递的对象是谁,要做什么,属于一种消息的传递,是事件响应(属性传值可以通过重写setter方法来实现事件的响应),但是单例和NSUserDefault不同,它们不像代理明确需要做什么,也不像通知可以传递消息,仅仅被用来存储和获取数据,并且,它们不知道谁存储的数据,也不知道谁来调用,这些事情需要我们自己主动去做。



七、单例

单例即在app生命进程中仅被初始化一次,它的内存地址在初始化时就确定了,所以我们可以在A页面存储数据,B页面取数据,并且值是相同的

注意:当app生命进程结束后,单例的内存空间也被释放了,数据会消失掉

使用:

首先我们需要新建单例对象

.h 文件
#import <Foundation/Foundation.h>
@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);

效果

body传值 ios ios传值方式_数据_04


八、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不删除,就一直存在,所以在使用这两种方式存取数据时要根据业务需求来。


九、总结

自己体会吧。。。啊哈哈哈