2011年10月19日

iPhone开发中两个UIViewController间传递变量和调用方法的处理

作者 非鱼

写这样的文章容易露怯,因为我不知道什么是业界真正规范的做法,这里写的只是自己认为比较简单易用的最佳实践,写出来恐怕会被大牛笑话,也影响自己的多年码农的形象。不过方法越多,新人越难掌握,所以提示一下也是好的,欢迎探讨。

其实这个题目里包含了两个东西,传递变量和调用方法,这两个的处理是不一样的,因为之前看了CocoaChina上的那篇文章是混在一起写的,所以这里也放在一篇文章里写。

变量传递

通常就是指要在一个ViewController里面读取到另外一个ViewController中的变量,比如弹出一个新界面的时候,需要知道原来的界面当前的SegmentControl现在选中的是哪个状态之类的,当然也有更复杂的,全局通用变量的处理也在这个范畴内,很多View都要用到同一个变量,比如当前登录用户的身份。

这里最简单的方式有两个,一是通过文件方式传递,当然,你不用自己实现读写文件,只要使用NSUserDefaults就可以了,它里面可以放进任何能够被序列化的简单变量,全局都可以读取,因为这个类本身是提供单例模式访问的,只要在任何地方修改了某个key对应的value,调用一下同步函数,这个值就被写入磁盘了,在任何其它的地方重新读取这个key,就能取到最新的value。而且这个值是永久保存的,程序重启也没关系。

第二个方法是通过appDelegate,它也是全局的,本身提供单例模式访问,所以不必担心访问到同一个类不同对象的不同变量值。只要你在AppDelegate.h里面定义的变量,在任何ViewController里面,获取[[UIAppcation sharedApplication] delegate],再调用它的该变量的名字就可以了,取值或者赋值都无所谓,该变量对整个应用程序都是可以访问的。系统提供的CoreData模板中Context的访问方式就是这样实现的。

还有第三个方式,跟下面的方法调用一起说。

方法调用

方法调用就是指在一个ViewController里面需要调用另外一个ViewController里面的某个方法,事件也可以归为这一类,比如当前端的这个ViewController完成了处理任务需要关闭的时候,要求它后面即将需要显示出来的那个ViewController刷新自己的页面,以便把最新的数据显示出来。

实现事件调用的办法最标准的是delegate。定义一个新的delegate接口,里面是某个方法或者事件的定义,不包含具体实现。被调用一方的ViewController实现该接口,并实现该方法的具体内容。调用一方需要声明该delegate类型的一个变量,在需要调用的时候,直接把这个变量当成被调用的那个Controller类来用,比如myDelegate.DoXXX(); 这样做了之后,在调用方的ViewController实例化的时候,需要将被调用方的实例赋值给它的myDelegate变量才行,要不然它就是个空指针了。这种实现方式之所以标准,是因为对调用方没有约束,这个类可以方便的重用,也就是松耦合设计,以后其它的项目可以把这个类复制过去,一行不用改,只要保证有别的类实现了这个delegate并被传递进来就可以了。

另外一种简化的方法是,直接将被调用方的类定义成调用方的一个变量。比如在ChildController里面定义一个变量RootViewController myRoot;然后实例化该类的时候直接child.myRoot=self;这时候在child内部,对myRoot这个变量是可以为所欲为的,它里面的方法你可以随便调用,包括刷新它自己的界面,或者改变某个控件的状态和值,或者改变它里面的某个变量的值(这就是上面说的传递变量的第三种方法,仅限于两个Controller之间),都是可以的。不过这时候就是紧耦合了,这个ChildController不方便拿到别的项目里去用,因为它里面把RootViewController的方法给写死了。不过如果你也没打算以后在别的项目里重用它,这样做可以省掉不少代码。不过同时你还得小心被你调用的那个rootController,此时有没有被系统给释放掉。如果在child里面定义变量或者传递变量过来的时候把它retain了,一般不会出现这种问题。

OK,就说这么多。