最近在输出本身从大一到如今学习iOS开发过程当中的相关内容,但愿你们star、fork支持!!! github.com/windstormey…java
这篇文章来梳理一番在iOS页面间的传值方式,说到页面传值,无论在任何平台开发中都是一个很是的重要的事情,这就让我想起了当初大一那会儿对Qt还不够熟悉,竟然对两个Window之间的传值用了一个全局变量来实现,而后在其它Window中显式声明一个extern标记变量做为数据源。emmm,如今想起来当初真的是蠢洁又扇凉。git
在iOS中的页面传值方式主要如下六种:程序员
在以上六种传值方式中,一、三、4使用得最为普遍,并且普遍存在与iOS SDK和各类第三方库中。github
属性传值不太经常使用于页面传值,由于经过属性传值有些时候须要顾及到各个页面的调用时机次序,反而经过属性设置一些页面的特殊功能标识的做用会更大。设计模式
举个🌰🍐!!!咱们首先要进行跳转的第二个页面设置属性值propertyPassValue。ruby
@interface NextViewController : UIViewController
@property (nonatomic, copy) NSString* propertyPassValue;
@end
复制代码
重写propertyPassValue的set方法,属性传值的写法还有其它方式,若是咱们对相关的对象作了lazy load,那么咱们应该在lazy load block中进行赋值。闭包
- (void)setPropertyPassValue:(NSString *)propertyPassValue {
_propertyPassValue = propertyPassValue;
self.tipsLabel.text = propertyPassValue;
}
复制代码
此时咱们经过实例化NextViewController,经过push或者present模态推出该vc便可看到对应的Label显示出propertyPassValue内容。函数
单例传值,说是传值实际上应该说是利用了单例的“持久化”状态来达到持久化属性的一种方法,单例应该说是一种设计模式,单例是一把很是好使的“武器”,放在会使的人手里,单例会变成一把利剑,可以很好的解决跨多个页面的值传递问题,可是若是放在不熟悉的人手里,颇有可能给他这个一高整个项目就散发着弄弄的java味了。🙂。(关于设计模式还会有一篇文章专门对其作了总结,你们能够持续关注。)工具
单例从字面上来理解,确定是不一样于往先alloc再init一个对象,最多见的用法就是NSUserDefault。(这个后边再说)post
+ (instancetype)shareInstance {
static instanceModel *model = nil;
if (!model) {
model = [[instanceModel alloc] init];
}
return model;
}
复制代码
经过以上代码咱们就建立出来了一个单例,实际上单例就是利用了static标识符告诉Xcode这个instanceModel变量要放在静态存储区,静态存储区是内存在程序编译的时候就已经分配好的,这块内存在App整个运行期间都将存在。它主要存放静态数据、全局数据和常量。
instanceModel是单拎出来的一个NSObject对象,以上代码只是建立出来一个存在整个App运行期间的单例,当咱们把App从后台kill掉后,整个单例也就不存在了,由于此时App的运行期已经结束了,若是你要持久化数据那就得使用其它方法(好比存文件)。
self.tipsLabel.text = [instanceModel shareInstance].instanceString;
复制代码
instanceString是多加的一个NSString对象,咱们经过其起到传值的做用。
NSUserDefault是苹果本来用于提供用户偏好设置的,可是我大天朝神奇的程序员怎么可能听命与你?因此在实际开发中NSUserDefault有时候起到了鬼斧神工的做用,而且咱们还可使用它来做为页面之间传值的工具。
// 设置NSUserDefault对应的k-v
[[NSUserDefaults standardUserDefaults] setObject:@"NSUserDefaults传值" forKey:@"NSUserDefaults"];
// 数据同步
[[NSUserDefaults standardUserDefaults] synchronize];
复制代码
而使用NSUserDefault也很是的简单,
self.tipsLabel.text = [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUserDefaults"];
复制代码
NSUserDefault底层是个文件,被苹果存在了对应的App沙盒中,只有咱们去使用过它才会出如今沙盒中。(沙盒实际上就是对应的App文件夹)所以除非咱们把这个App删掉和手动清洗掉对应的数据,不然咱们对NSUserDefault设置的value将一直存在,同时它也是数据持久化的一直方式。在使用NSUserDefault做为页面间传值的工具时,千万要注意记得要手动显式调用NSUserDefault的数据同步方法,并且使用的时候尽可能避免同时对一个key进行同时“写”操做。(不过也没人这么无聊对一个用户偏好key同时写吧?🙂)
代理传值我能够拍着胸脯说这绝对是目前iOS中使用范围最广和使用次数最多的传值方式,并且基本上每个VC的编写都会接触到代理,并且代理传值对于我本身来讲也是一个很是经常使用的反向传值方式。
同时代理也是一种设计模式,第一次接触代理模式的同窗(包括我)都会有些摸不着头脑,虽说是和平常生活中找中介租/买房子很是像,可是转换到代码层面就是迷迷糊糊。🙂。先po一张图,
从上图中咱们能够看到,需求方至关因而代理制定者,而受理方则是代理实现者(说得我都懵逼了😂)。直接瞅代码吧,
// 协议
@protocol NextViewControllerDelegate <NSObject>
- (void)passValueOfProtocol:(NSString *)string;
@end
@interface NextViewController : UIViewController
@property (nonatomic, weak) id<NextViewControllerDelegate> vcDelegate;
@end
复制代码
从以上代码中,咱们看到了制定代理使用@protocol关键字便可,@protocol中的内容能够认为是“合同内容”,咱们还须要声明一个id指针变量vcDelegate,这个变量能够认为是“合同书”自己,而如何让需求方发布这个协议或者合同呢?
[_vcDelegate passValueOfProtocol:self.tipsLabel.text];
复制代码
在须要的地方经过id指针变量_vcDelegate调用passValueOfProtocol方法便可,参数即为要填入合同书的内容🙂。经过显式调用以上方法便可完成所谓的“填写合同书”环节。接下来,咱们要在对应的类中的遵照协议(合同书)便可使用需求方填入合同书中的内容。
// 写明要遵照的协议
@interface ViewController () < NextViewControllerDelegate>
@end
复制代码
NextViewController *vc = [[NextViewController alloc] init];
// 写明要受理的代理对象
vc.vcDelegate = self;
复制代码
// 要实现的代理方法
- (void)passValueOfProtocol:(NSString *)string {
self.tableView.headTitleLabel.text = string;
}
复制代码
受理方遵照协议的过程千万要注意设置代理对象,vc.vcDelegate = self;
,若是你忘了设置对应的代理对象,而只是实现了协议方法,这就至关于咱们把合同给了对方而已,对方并未签名,这份合同对甲乙双方其实是无效的!所以千万别忘了设置代理对象。经过以上步骤,咱们便可从需求方拿到数据,从而显示在代理方,是一种很是有效(可是有些麻烦)的反向传值方法。
block,是苹果这几年来强烈推荐的回调传值方式。你能够认为是C中匿名函数,C++/java中的lambda表达式,JS中的闭包等。只不过在iOS中换了个说法称之为block(我是这么认为的🙂)。
@interface NextViewController : UIViewController
@property (nonatomic, copy) NSString* propertyPassValue;
@property (nonatomic, weak) id<NextViewControllerDelegate> vcDelegate;
@property (nonatomic, copy) void (^passValueBlock)(NSString *);
@end
复制代码
在此咱们把block做为了一个属性,而赋值block,
self.passValueBlock(self.tipsLabel.text);
复制代码
咱们只须要在对应的地方给block传入NSString类型参数便可。而使用block,咱们在对应的VC中参考如下代码便可获取到对应的值。
NextViewController *vc = [[NextViewController alloc] init];
vc.passValueBlock = ^(NSString *string) {
self.tableView.headTitleLabel.text = string;
};
复制代码
以上只是block的简单使用,关于block更加细节的一些用法,你们能够参考这篇文章(操蛋的block语法🙂)。
通知也是一把利器,尤为是涉及跨多个页面间的传值时它的好处就提现出来了,并且最重要的是在项目中巧妙的使用通知可以打破以往纵向传递的繁杂性,从而达到一种“星型”发射状的模型。之因此这么说是由于通知能够说是最切合真正的面向对象的核心,真正的面向对象语言——smalltalk,若是没记错的话,smalltalk应该是一切面向对象语言的开山鼻祖吧😀。面向对象的真正核心应该是“一切操做皆消息”,也就是说,就算是简单加法、减法也是经过“发送消息”完成的,而不是像各类课本及老师说的什么继承、多态等等这些外壳,这些外壳用C也可以实现,根本就不算是面向对象语言的标志,反观如今好比C++、java这些当初跟smalltalk分道扬镳的语言,现现在的更新也都是在愈来愈像smalltalk罢了。(包括ruby🙂),OC就是基于smalltalk的封装,不过不少东西也没拿完。
而在iOS中实现通知得益于苹果爸爸的高度封装能力,把不少问题都给咱们搞定了,
[[NSNotificationCenter defaultCenter] postNotificationName:@"notify" object:nil userInfo:@{@"notify" : self.tipsLabel.text}];
复制代码
经过以上代码,咱们就注册了一个名为notify的通知,其带有了一个key为notify且value为self.tipsLabel.text的字典。使用通知须要有一个通知中心做为沟通的桥梁,发送通知的object为告诉通知中心要把这个通知消息发送给哪一个对象,若是想要群发消息通知,那就nil。
而接收通知,咱们须要给当前类添加一个观察者,用于监听对应name的通知。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notifyMesg:) name:@"notify" object:nil];
复制代码
以上代码中的object参数为接受哪一个对象发送而来的消息,若是咱们想接收任何对象发送而来的消息那就nil吧。接着,咱们在处理消息通知的notifyMesg:方法中取得消息实体,
- (void)notifyMesg:(NSNotification *)notify {
self.tableView.headTitleLabel.text = notify.userInfo[@"notify"];
}
复制代码
固然,咱们对当前类添加了观察者去监听某个通知,那就要在适当的实际去取消监听,固然你彻底能够不用移除通知,可是若是多个消息通知重叠在一个项目中时,很容易就致使消息的监听迷之问题,由于一旦上升到通知层面的debug那就不是线性的了,可想而知难度得有多大。🙂。通常来讲咱们会在当前类的dealloc方法中移除通知。