iOS - 对象间传值
传值方式
个人把他们分为官方和非官方,可以看出官方版的传值方式均为两个对象之间通过系统特定的方法来进行数据传递,而非官方版则是通过一个中间件来进行数据传递
官方版:
① delegate 代理
② block 代码块
③ NSNotification 通知
④ KVC 键-值编码
⑤ KVO 键值观察模式
⑥ 属性直接传值
非官方版:
① 单例类
② 数据缓存(如 NSUserDefaults 不推荐)
delegate
它是一种设计模式,代理者通过实现被代理这定义的代理协议,来进行通信传递消息,在iOS中主要用于视图与使用这个视图的对象之间来进行交互,可以有返回值
注意:delegate
和 block
都是一对一的关系 他们和通知不一样 通知是一对多 delegate
设置属性是用assign
或weak
修饰 不要改变其引用计数 避免和代理者之间出现循环引用
block
概念: Block
是一个C级别的语法以及运行时的一个特性,和标准C中的函数(函数指针)类似,但是其运行需要编译器和运行时支持,可作为参数进行传递用于回调,block
可以定义在方法里,函数不能。block
语法简单,写在方法里可以访问局部变量可以使代码更加的紧凑,结构化。相对于 delegate
,block
不用建立代理协议,使用简单,它的实现具有封闭性(closure),而又能够很容易获取上下文的相关状态信息。从ios4.0开始就很好的支持Block
。
用途:常用于逆向传值、作为方法参数、作为类的属性、作为全局变量
注意:
1、在使用block
前需要对block
指针做判空处理。不判空直接使用,一旦指针为空直接产生崩溃。
2、在block
使用时为什么要用copy
修饰 因为block
变量默认是声明为栈变量的 block
本身是像对象一样可以retain
,和release
。但是,block
在创建的时候,它的内存是分配在栈上的,而不是在堆上。他本身的作于域是属于创建时候的作用域,一旦在创建时候的作用域外面调用block
将导致程序崩溃。因为栈区的特点就是创建的对象随时可能被销毁,一旦被销毁后续再次调用空对象就可能会造成程序崩溃,在对block
进行copy
后,block
存放在堆区.
3、使用方将self
或成员变量加入block
之前要先将self
变为weakSelf
如果不是self
引用的block
则不需要 比如数组枚举遍历的block
4、在多线程环境下(block
中的weakSelf
有可能被析构的情况下),需要先将self
转为strong
指针,避免在运行到某个关键步骤时self
对象被析构。
意义:Block
是iOS4.0+ 和Mac OS X 10.6+ 引进的对C语言的扩展,用来实现匿名函数的特性。它允许开发者在两个对象之间将任意的语句当做数据进行传递,往往这要比引用定义在别处的函数直观,
NSnotification
概念:NSnotification
一个中心对象注册和发送通知,所用的其他的对象都可以收到通知。
用途:常常用于在向服务器端请求数据或者提交数据的场景,在和服务器端成功交互后,需要处理服务器端返回的数据,或发送响应消息等
注意:它是同步的消息通知机制,只有Observer
将消息处理完毕后,消息发送者才会继续执行,因此在通知处理的地方做大量耗时操作的话,就会带来卡顿的问题啦。
在多线程的应用中,Notification
在哪个线程中Post
, 就是在那个线程分发,也就在同一个线程中被observer
处理。而通常呢,我们会在Observer
对象的dealloc
方法中去removeObserver
,理论上,如果observer
的dealloc
和消息发送者的postNotification
的方法在不同的线程中调用的话,是有可能会导致Crash的。
意义:广播数据,一对多
KVC
概念:Key-value coding
,它是一种使用字符串标识符,一个非正式的 Protocol
,间接访问对象属性的机制,而不是直接调用getter
和 setter
方法是通过 set value for key
进行间接访问实例变量。
用途:动态访问你要存取的类的属性、动态访问和修改私有变量
注意:可以1对1,也可以1对多。
意义:提供一种机制来间接访问对象的属性
KVO
概念:KVO,被观察者添加观察者,被观察者使用KVC键值编码来修饰它的实例变量,当被观察者发生改变时,观察者就能侦听到被观察者改变的事件,从而做出相应。
注意:
-
addObserver
之后,在不需要监听时,及时remove
,否则被监听对象释放后,再触发监听器会引起crash
-
addObserver
方法与removeObserver
方法要一一对应。不要重复添加监听,也不要remove
没有添加过的监听。
用途:对指定对象的某个属性进行观察,当属性发生变化时,进行通知.
意义:提供了一种当其它对象属性被修改的时候能通知当前对象的机制
单例模式
单例模式大概是设计模式中最简单的一个,没什么好说的,就是保证一个类只有一个实例,并且提供一个全局的访问入口访问这个实例。正式因为它的这个特性,我们可以通过他来作为一个中间件来进行传值(其实也不算是传值,类似于前边对象set 后边对象get)原理跟缓存到本地沙盒基本相同。
注意:单例虽好 但也有坑
1、多线程
异步多线程的时候,假如同时执行到创建的那一行,这时候可能会创建多个不同的对象,所以这里可以加锁或同步块,但这不是OC创建单例最高效的方式,有一种性能是他几十倍的方法就是使用dispatch_once,关于性能的比对,大神们做过实验和分析。请参考这里
2、内存问题
单例模式实际上延长了对象的生命周期。那么就存在内存问题。因为这个对象在程序的整个生命都存在。所以当这个单例比较大的时候,总是hold住那么多内存,就需要考虑这件事了。另外,可能单例本身并不大,但是它如果强引用了另外的比较大的对象,也算是一个问题。别的对象因为单例对象不释放而不释放。
当然这个问题也有一定的办法。比如对于一些可以重新加载的对象,在需要的时候加载,用完之后,单例对象就不再强引用,从而把原先hold住的对象释放掉。下次需要再加载回来。
3、循环依赖
在开发过程中,单例对象可能有一些属性,一般会放在init的时候创建和初始化。这样,比如如果单例A的m属性依赖于单例B,单例B的属性n依赖于单例A,初始化的时候就会出现死循环依赖。死在dispatch_once里。
数据缓存
iOS本地缓存数据方式有五种:
1、直接写文件方式:可以存储的对象有NSString、NSArray、NSDictionary、NSData、NSNumber
,数据全部存放在一个属性列表文件(*.plist文件)中。
2、NSUserDefaults
(偏好设置),用来存储应用设置信息,文件放在perference
目录下。
3、归档操作(NSkeyedArchiver)
,不同于前面两种,它可以把自定义对象存放在文件中。
4、coreData:coreData
是苹果官方iOS5之后推出的综合型数据库,其使用了ORM(Object Relational Mapping
)对象关系映射技术,将对象转换成数据,存储在本地数据库中。coreData
为了提高效率,甚至将数据存储在不同的数据库中,且在使用的时候将本地数据放到内存中使得访问速度更快。我们可以选择coreData的数据存储方式,包括sqlite、xml
等格式。但也正是coreData
是完全面向对象的,其在执行效率上比不上原生的数据库。除此之外,coreData
拥有数据验证、undo
等其他功能,在功能上是几种持久化方案最多的。
5、FMDB:FMDB
是iOS平台的SQLite
数据库框架,FMDB
以OC的方式封装了SQLite
的C语言API
,使用起来更加面向对象,省去了很多麻烦、冗余的C语言代码,对比苹果自带的Core Data
框架,更加轻量级和灵活,提供了多线程安全的数据库操作方法,有效地防止数据混乱。
总结:数据缓存这里就不过多赘述了,但无论使用哪种方式进行缓存存取,道理是相同的,请参考单例传值,不过如果仅仅是对象之间的传值,强烈不建议使用缓存!!!总而言之,传值方式要根据场景自己来定,了解各种方式才可以更好的帮你完成对象间值的传递。