深入了解iOS中VC切换的传值方式

由于上次面试中有提到相关内容,所以这次我专门深入研究了iOS的几种方式:
首先把所有的传值方式都列出来,如果有遗漏,请指正


首先列出iOS中使用的传值方式:

  1. init 传值(即在创建VC的时候就对响应的参数进行设置)
  2. property 传值(即属性赋值)
  3. Router 传值(这个在OC中被使用,因为作者没有写Swift版本,所以先开个坑,估计我会填坑)
  4. Delegate 传值(通过协议和代理传值)
  5. 闭包(block)传值 (通过swift中的闭包,类似于OC中的block传值)
  6. Notification 传值 (通过消息中心进行传值)
  7. KVO 传值 (通过观察者模式进行传值)
  8. AppDelegate 传值 通过Appdelegate进行传值
  9. NSUserDefault 传值 通过NSUserDefault传值
  10. 通过static进行全局变量的存储,这里就不再进行演示

基本上总结起来就是以上10种,接下来我会一种种介绍,同时会讲明传值方式中所遇到的坑。

本文中的所有代码已经放到github上,我会在文章最后加上github地址,所以在文章中只展示主要的代码。

一、init 传值

这个解释起来很简单,就是在VC对象init的时候,通过调用自定义的init函数,从而将所需要的参数传入对应的对象内。

实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
//源VC
func jumpToNextVC() {
let goal = TVGoalViewController.init(text: self.input.text!)
navigationController?.pushViewController(goal, animated: true)
}
//目标VC
convenience init(text:String) {
self.init(nibName:nil, bundle:nil)
self.text.text = text
}

很明显,这种传值方式有着较高的耦合,对后期的维护有一定的影响,所以个人不建议。(虽然我在一开始写代码的时候很不要脸的这么干的~~)

二、property 传值(即属性赋值)

property传值就是通过在创建完VC后,对VC中的每个属性都单独复制。

1
2
3
4
5
6
7
8
//源VC:
func jumpToNextViewController() {
let goal = TVGoal2ViewController()
goal.showlabel.text = self.input.text
self.navigationController?.pushViewController(goal, animated: true)
}

优缺点:虽然相较上一种方式来说,解了耦合,但是因为要在原VC中进行赋值,所以需要目标VC中的相应属性设置为Public,这对于代码的安全性有一定的影响。

三、Router 传值

这种方式在OC中有一定的用到,但是比较冷门,主要的方式通过一个单独的类来保存所有需要跳转的VC,同时将值保存在对应的params。从而通过类的函数对类进行转换,这个因为原作者只有OC版本,所以这里就不贴上OC代码,估计我会在接下来几天将这个函数移植到swift上。

优缺点:由于通过类来保存需要的内容,这样就解耦了,不过由于要引入第三方类,所以操作相对而言比较繁琐,而且需要学习第三方框架的函数,所以有一定的学习成本。

四、Delegate 传值

这种方式在iOS开发中是比较常见的几种方法之一,而且苹果官方也大量的使用这个方法,比如说AppDelegate。同时在UITextField,UIScrollView等控件中也经常性的使用这种方法传值。

苹果官方对Delegate有以下的解释:

Delegation is a simple and powerful pattern in which one object in a program 1 acts on behalf of, or in coordination with, another object. The delegating object keeps a reference to the other object—the delegate—and at the appropriate time sends a message to it. The message informs the delegate of an event that the delegating object is about to handle or has just handled. The delegate may respond to the message by updating the appearance or state of itself or other objects in the application, and in some cases it can return a value that affects how an impending event is handled. The main value of delegation is that it allows you to easily customize the behavior of several objects in one central object.

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//原VC文件
protocol LoginDelegate {
func changeText(name:String)
}
class TVLoginViewController: UIViewController {
@IBOutlet weak var label: UITextField!
@IBOutlet weak var loginButton: UIButton!
var delegate:LoginDelegate?
override func viewDidLoad() {
super.viewDidLoad()
self.loginButton.addTarget(self, action: #selector(clickLogin), forControlEvents: UIControlEvents.TouchUpInside)
}
func clickLogin() {
self.delegate?.changeText(self.label.text!)
self.navigationController?.popViewControllerAnimated(true)
}
}
//目标VC文件
class TVDelegateViewController: UIViewController,LoginDelegate{
@IBOutlet weak var welcomeLabel: UILabel!
@IBOutlet weak var login: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
login.addTarget(self, action: #selector(next), forControlEvents: UIControlEvents.TouchUpInside)
}
func changeText(name: String) {
self.welcomeLabel.text = "欢迎回来:"+name
}
func next() {
let goal = TVLoginViewController()
goal.delegate = self
self.navigationController?.pushViewController(goal, animated: true)
}
}

优缺点:个人来说,我比较看好delegate传值,因为他结构明显,清晰的语法定义,减少维护成本,较强的代码可读性。 同时减少代码的耦合性,使事件监听和事件处理相分离。 不需要创建第三方来监听事件和传输数据。 并且一个控制器可以实现多个代理,满足自定义开发需求,可选必选有较大的灵活性。但是由于需要创建协议,再进行编码,可能代码量比较大。

五、闭包传值

闭包传值类似于oc中的block传值。而苹果官方对于Block的解释是这样的:

A block is an anonymous inline collection of code, and sometimes also called a “closure”.
Blocks are a powerful C-language feature that is part of Cocoa application development. They are similar to “closures” and “lambdas” you may find in scripting and programming languages such as Ruby, Python, and Lisp. Although the syntax and storage details of blocks might at first glance seem cryptic, you’ll find that it’s actually quite easy to incorporate blocks into your projects’ code.

这说明闭包在实际使用中有着较大的使用面,不仅仅在oc中,在ruby,python,lisp中均有使用。同时由于实现它所需要的代码量相对于delegate更少,所以很多人都喜欢用(不过我不太喜欢)。
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//原VC文件:
typealias returnClosure = (string:String)->Void
...
func login() {
if closureValue != nil {
closureValue!(string: self.input.text!)
}
self.navigationController?.popViewControllerAnimated(true)
}
//目标VC文件
func myClosure(string:String) -> Void {
self.welcomeLabel.text = "欢迎:"+string
}
func jump() {
let goal = TVLogin1ViewController()
goal.closureValue = myClosure
self.navigationController?.pushViewController(goal, animated: true)
}

六、Notification传值

Notification也是和上面的Delegate一样,在iOS开发中经常使用,他通过使用设计模式中的观察者模式,不过当其中一个内容进行变化的时候,通过NotificationCenter来接受内容变化,从而告诉需要告诉的人,原值已经发生改变了。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//原VC文件:
func logYouName() {
let notific = NSNotification.init(name: "loginNameNotification", object: self, userInfo: ["name":name.text!])
NSNotificationCenter.defaultCenter().postNotification(notific)
self.navigationController?.popViewControllerAnimated(true)
}
//目标VC文件:
func next() {
let goal = TVLogin2ViewController()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(loginName), name: "loginNameNotification", object: goal)
self.navigationController?.pushViewController(goal, animated: true)
}
func loginName(notification:NSNotification) {
let dic = notification.userInfo
self.welcomeButton.text = "欢迎:"+(String)(dic!["name"]!)
NSNotificationCenter.defaultCenter().removeObserver(self)
}

优缺点:Notificatoin使用了设计模式中的观察者模式,这种模式的好处就是:使用简单,代码精简。同时解决了同时向多个对象监听相应的问题,当需要调用修改的内容的时候也十分方便。

七、KVO 传值

这种传值和Notification的传值方法想法上面基本一致,都是通过设计模式中的观察者模式来进行实现的。不过区别是,一般来说Notification是作为一个第三者的单例来进行实现,而KVO是直接通过一个对象来观察另一个对象的值是否发生改变,观察者本身和被观察者不一定是要VC,只要是继承了NSObject即可实现该方法。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//原来VC文件
func login() {
self.setValue(input.text, forKey: "username")
self.navigationController?.popViewControllerAnimated(true)
}
//目标VC文件:
override func viewDidLoad() {
super.viewDidLoad()
loginButton.addTarget(self, action: #selector(login), forControlEvents: UIControlEvents.TouchUpInside)
goal.addObserver(self, forKeyPath: "username", options: [NSKeyValueObservingOptions.New,NSKeyValueObservingOptions.Old], context: nil)
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if keyPath == "username" {
self.welcomeLabel.text = "欢迎"+(String)((change!)["new"]!)
}
}

优缺点:优缺点和Notification大相近庭,不过在这里面使用的时候有个坑,当你在input输入相应的文字内容的时候,你需要在点击log之后,将文本中的内容进行保存,因为观察者不会对UITextField中的内容进行观察,所以很有可能发生问题。(个人不建议使用这个方法来用来观察需要手动输入的变量)

八、AppDelegate 传值

这个方法基于iOS App的实际创建方法,通过AppDelegate来唤起整个App,从而使得AppDelegate可以操控整个App,同时作为总控制,所有内部的类都能够通过UIApplication来获取Delegate,从而得到AppDelegate。所以可以通过他来进行值的传输。
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//源VC文件
func next() {
let next = TVGoal3ViewController()
let selfDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
selfDelegate.storeString = self.input.text!
self.navigationController?.pushViewController(next, animated: true)
}
//目标VC文件
override func viewDidLoad() {
super.viewDidLoad()
let app = UIApplication.sharedApplication()
let selfDelegate = app.delegate as! AppDelegate
showLabel.text = selfDelegate.storeString
self.backButton.addTarget(self, action: #selector(goBack), forControlEvents: UIControlEvents.TouchUpInside)
}

优缺点:由于通过AppDelegate来实现,基本上内容都保存在AppDelegate中,所以调用起来方便,由于都保存在那里面,导致整个AppDelegate显得臃肿,不方便与以后的维护。

九、NSUserDefault 传值

苹果提供给开发者一个NSUserDefault这个系统变量,开发者可以通过这个来将内容保存到整个系统中,从而方便下一次的调用,这个和AppDelegate有异曲同工之妙。
代码如下:

1
2
3
4
5
//源文件NSUserDefaults.standardUserDefaults().setObject(self.input.text, forKey: "store")
//目标文件
let userdefault = NSUserDefaults.standardUserDefaults()
self.showLabel.text = (String)(userdefault.valueForKey("store")!)

优缺点:主要和AppDelegate一样,不过因为是系统级的变量,所以他所给予我们的内存空间是有限的,不能存放太大的资料。(记得存储完同步下,方便以后继续使用)

十、全局变量Static 传值

这个和一般语言中都一样,由于Static存在于全局存储区(静态存储区),只要程序不退出,那么就会一直保存起来。这样的一个好处就是你可以反复使用,但是由于不释放,所以如果量一大,容易发生内存不足~~所以不建议使用。

总结:以上就是所有10种iOS中的常见传值方式,他们各有各的优点,缺点,所以具体到底如何使用,还是需要开发者自己甄别。

最后贴上Github地址