知识点大纲
(1) 什么是界面传值?
(2) 正向传值
(3) 代理传值
(4) 单例传值
(5) 通知传值
(6) 代码块传值
1.什么是界面传值?
绝大多数应用都是由多个界面构成的,需要在界面之间传输数据,这就是界面传值。
2. 正向传值
实例:登陆界面创建主界面,登陆界面的用户名传递到主界面
分析:A界面创建B界面,A界面的值传递到B界面
如何实现?
》在B界面中添加属性,用来保存用户名
@interface MainViewController : UIViewController
// 定义了可以接受其他界面传过来的值
@property (nonatomic,copy) NSString *userName; @end
》A界面中
// 主界面
MainViewController *mainVC = [[MainViewController alloc] init];
// 注意:界面切换前传值
mainVC.userName = @"hehe";
// 跳转
[self presentViewController:mainVC animated:YES completion:nil];
》B界面中
//显示传过来的用户名
UILabel *nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 50, 200, 30)]; nameLabel.text = [NSString stringWithFormat:@"用户名是: %@",_username]; [self.view addSubview:nameLabel];
3.反向传值(代理)
代理传值六步走:
// A界面创建B界面, B把值(颜色)传回给A界面
MainViewController ConfigViewController
(1) 第一步,制定协议(哪里通知代理) //因为是ConfigViewController其他界面发送消息 // 1. 告诉其他界面我要给你发送什么消息 // 2. 确保其他实现了消息对应的方法 // 声明协议和协议方法 @protocol ConfigViewControllerDelegate <NSObject>
// 最好以类型开头+方法名 // 在遵守协议的其他类实现方法(即只声明不实现) -(void)conifgViewControllerWithBackgroundColor:(UIColor *)color; @end |
|
@interface ConfigViewController : UIViewController
(2) 第二步,定义属性 // 作用: 保存主界面的指针 // 为什么保存, 最后给主界面发送消息修改字体 // 细节1: weak 确保不会循环引用 // 细节2: id 如果是id, 能传入任意界面了 // 细节3: 遵守协议意味着可以任意对象 @property (nonatomic,weak) id<ConfigViewControllerDelegate> delegate; @end |
|
(3) 第三步,通知代理 // 通知代理(其实就是调用方法) // 判断是否实现了代理方法 if ([self.delegate respondsToSelector:@selector(changeFontSize:)]) { [self.delegate changeFontSize:fontTextField.text.intValue]; } |
|
(4) 第四步,遵守协议 @interface MainViewController () <ConfigViewControllerDelegate>
|
|
//切换到配置界面 ConfigViewController *configVC = [[ConfigViewController alloc] init]; (5) 第五步,设置成为代理 // 作用: 把当前界面的指针传到configVC中 // 为啥: 希望配置界面中背景颜色改变通知主界面 configVC.delegate = self; [self presentViewController: configVC animated:YES completion:nil]; |
|
(6) 第六步,实现代理方法(具体方法的实现) - (void)conifgViewControllerWithBackgroundColor:(UIColor *)color{ NSLog(@"实现的代理方法"); self.view.backgroundColor = color; } |
》在声明代理属性时
// 不能使用strong,避免VC调用sVC,然后sVC再调用VC形成循环引用,内存无法释放
@property (nonatomic,weak) id<SecondViewControllerDelegate> delegate;
(4) 单例传值(1对N)
需求:A(登陆)->B->C->D->E->F(用户信息)
需要A中的数据在F界面中显示;
需要在每个界面中都显示“今天脱口秀”;
解决:使用单例传值
/** 懒汉式(真正用到的时候才加载) 饿汉式(程序一启动就加载)
* 单例模式顾名思义就是只有一个实例,它确保一个类只有一个实例,并且自行实例化并向整个系统提供这个实例。它经常用来做应用程序级别的共享资源控制。这个模式使用频率非常高,通过一个单例类,可以实现不同view之间的参数传递
*/
实现:
》创建一个单例类DataClass @interface DataClass : NSObject
// 定义要使用的属性
@property (nonatomic,copy) NSString *user;
@property (nonatomic,strong) NSString *passwd;
// 类方法
+ (instancetype)shareData;
@end
// 关键 // 第一次执行: shareData为空, 会申请一个对象 // 以后执行: shareData不为空, 直接返回以前创建的对象 static DataClass *shareData = nil; // 表示获取一个共享的实例 + (instancetype)shareData{ if (shareData == nil) { shareData = [[DataClass alloc] init]; } return shareData; }
// alloc方法内部会调用这个方法 // 最好实现重写这个方法,避免在实例化的时候用到了alloc + init + (instancetype)allocWithZone:(struct _NSZone *)zone{ if (shareData == nil) { shareData = [super allocWithZone:zone]; } return shareData; } //要求非常高单例类往往重写 // alloc,dealloc,retain,release, // autorelease,copyWithZone
》存储值 DataClass *sharData = [DataClass shareData]; sharData.user = @"单例模式"; sharData.passwd = @"123321"; 》取值 DataClass *dataShare = [DataClass shareData]; NSLog(@"%@",dataShare);
(5) 通知传值(1对N)
A - B - C - D - E - F - G(通知A为红色,B界面为蓝色,C界面为黑色…) 需求: 配置界面要换皮肤(背景颜色),其他界面相应 实现: 》配置界面中,发送换皮肤的通知 // 获取系统通知类的单例对象 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; // 发出一个通知 // 参数Name: 通知的名字 // 参数object: 要发给那个对象, nil表示不限制对象 // 参数userInfo: 通知附加的信息, 皮肤颜色传过去 // 原型[center postNotificationName:(NSString *) object:(id) userInfo:(NSDictionary *)]; [center postNotificationName:@"color" object:nil userInfo:@{@"color":[UIColor magentaColor]]; 》其他界面获取到通知,切换背景 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; // 获取通知 // 参数addObserver: 观察者,监听者 // 参数selector: 获取到通知后的处理方法 // 参数name: 通知的名字 // 参数object: 要接受那个对象,nil表示不限制对象 [center addObserver:self selector:@selector(centerTest:) name:@"color" object:nil]; // 注意: 参数类型不是NSNotificationCenter - (void)centerTest:(NSNotification *)notification{ // 获取通知中附加的信息 NSLog(@"FirstViewController >> %@",notification.userInfo[@"color"]); self.view.backgroundColor = notification.userInfo[@"color"]; } 给不同界面发送通知 [center postNotificationName:@"button" object:nil userInfo:@{@"button":@"yes",@"fVC":[UIColor brownColor],@"sVC":[UIColor cyanColor],@"tVC":[UIColor orangeColor]}];
(6) 代码块传值
1> 如何定义block变量(block是一种数据类型) int (^sumBlock)(int, int); void (^myBlock)();
代码块传值三步走:
// A界面创建B界面, B界面把背景颜色传回给A界面 实现: 》第一步 声明代码块变量,并声明set方法 @property (nonatomic,copy) void (^changeBackColor)(UIColor *color); - (void)setChangeBackColor:(void (^)(UIColor *))changeBackColor;
三小步: > 模拟要实现的功能方法 // 模拟要实现的功能 - (void)changeBackColor:(UIColor *)color{ self.view.backgroundColor = color; } > 将方法转为函数 // 将方法转为函数 void changeBackColor(UIColor *color){ self.view.backgroundColor = color; } > 将函数转为代码块 // 将函数转为代码块 void (^changeBackColor)(UIColor *color) = ^(UIColor *color){ self.view.backgroundColor = color; }; 》第二步 代码块的调用 __weak typeof(self) weakSelf = self; // 代码块的调用,设置背景 if (weakSelf.changeBackColor) { // 判断代码块是否为空 NSArray *colors = @[[UIColor yellowColor], [UIColor blueColor], [UIColor greenColor]]; // 代码块的调用 // 产生随机数arc4random() weakSelf.changeBackColor(colors[arc4random()%3]); } 》第三步 设置代码块中具体操作 // 把代码块作为参数传递 [mVC setChangeBackColor:^(UIColor *color) { self.view.backgroundColor = color; }];
/**
1、默认strong,可选weak。strong下不管成员变量还是property,每次使用指针指向一个对象,等于自动调用retain(), 并对旧对象调用release(),所以设为nil等于release。
2、只要某个对象被任一strong指针指向,那么它将不会被销毁,否则立即释放,不用等runloop结束。所有strong指针变量不需要在dealloc中手动设为nil,ios会自动处理,debug可以看到全部被置为nil,最先声明的变量最后调用dealloc释放。
3、官方建议IBOutlet加上__weak,实际上不用加也会自动释放;
4、优先使用私有成员变量,除非需要公开属性才用property。
5、避免循环引用,否则手动设置nil释放。
6、block方法常用声明:@property (copy) void(^MyBlock)(void); 如果超出当前作用域之后仍然继续使用block,那么最好使用copy关键字,拷贝到堆区,防止栈区变量销毁。
7、创建block匿名函数之前一般需要对self进行weak化,否则造成循环引用无法释放controller:
__weak typeof(self) weakSelf = self;
引用实例变量也会造成self的强引用,实例变量要用weakSelf->实例变量 的方式来访问
*/