自定义生命周期的设计(iOS篇)

首先要确定一点,我们的App,要基于XIB文件进行编程,而不是在每个相应的ViewController里面去手动创建页面的每个控件。这样做的好处是,将页面布局与业务逻辑彻底隔离。于是我们可以把xib的绘制工作交给美工人员,而iOS程序员,主要关心的是业务逻辑。

有人会怀疑过多的xib会导致App体积变大,我曾经有专门看过ipa文件解压后的文件列表,我发现每个xib也就2k大小,而一个App最多也就七八十个xib,那么就是说共计150k大小的样子,由于是xml文本文件,所以压缩后更小。而相比较下,占用App体积最多的,往往是开机画面图,引导图这些东西,如果真的想要App瘦身,应该在图片上进行优化,而不是不使用xib直接布局。

另一个需要明确的是,在一开始创建ViewController的时候,不要同时创建xib文件,因为这样子的话,就在xib中把ViewController和xib进行关联了,而我们要做的是解耦,这显然不合理。所以正确的流程是,分开创建ViewController和xib,不要进行管理。在ViewController的初始化中,加载xib文件,如下所示:

接下来要做的事情,有时候连我都觉得匪夷所思。我们先来看一段代码:

 
 1 #import "APageViewController.h"
2
3 @interface APageViewController ()
4
5 @end
6
7 @implementation APageViewController
8
9 - (void)loadView
10 {
11 [super loadView];
12 // Do any additional setup after loading the view.
13
14 //1.从xib中获取View
15 NSArray* list = [[NSBundle mainBundle] loadNibNamed: @"APageView" owner: self options: nil];
16 self.view = list.lastObject;
17
18 UILabel* nameLabel = (UILabel*)[self.view viewWithTag: 100];
19 nameLabel.text = @"";
20
21 UILabel* ageLabel = (UILabel*)[self.view viewWithTag: 200];
22 ageLabel.text = @"";
23
24 UIButton* getInfoButton = (UIButton*)[self.view viewWithTag: 300];
25 [getInfoButton addTarget: self action: @selector(getInfo) forControlEvents:UIControlEventTouchUpInside];
26
27 UIButton* clearInfoButton = (UIButton*)[self.view viewWithTag: 400];
28 [clearInfoButton addTarget: self action: @selector(clearInfo) forControlEvents:UIControlEventTouchUpInside];
29 }
30
31 - (void) getInfo {
32 UILabel* nameLabel = (UILabel*)[self.view viewWithTag: 100];
33 nameLabel.text = @"包小强";
34
35 UILabel* ageLabel = (UILabel*)[self.view viewWithTag: 200];
36 ageLabel.text = @"31.6";
37 }
38
39 - (void) clearInfo {
40 UILabel* nameLabel = (UILabel*)[self.view viewWithTag: 100];
41 nameLabel.text = @"";
42
43 UILabel* ageLabel = (UILabel*)[self.view viewWithTag: 200];
44 ageLabel.text = @"";
45 }
46
47 - (void)dealloc {
48 [super dealloc];
49 }
50
51 @end
 

上面的代码,是再普通不过的一段代码,读取一个xib,获取到View的句柄,初始化其中的每个控件,为按钮挂上点击后的方法事件,使得按钮变红。内部还有个计数器变量,每次点击按钮都会加1。巧的是,恰好还要侦听一个通知(Notification)。最后,调用API。

我们发现,有2个问题:

1)在willDidLoad中做了太多的事情,又是初始化变量,又是初始化控件,又是给按钮挂事件,注册通知,还要调用API。

2)每次使用控件时,都要根据在xib中指定的tag重新获取,而iOS中的控件tag值,只能是整数。

我们的解决方案是,既然页面每次加载都会调用loadView和viewDidLoad方法,每次销毁都会调用dealloc方法,那么干脆就在基类BaseViewController重写了这几个方法,于是现在页面的生命周期如下所示:

相应的基类代码请参见本章的源码。

我们在每个页面都会重写createFields、loadData这些方法,每个方法的意义如下:

1)createFields和destroyFields: 创建/销毁页面级变量的地方。

2)createViews和destroyViews: 创建/销毁页面内控件的地方。

3)createEvents和destroyEvents: 创建/销毁页面内事件、通知的地方。

4)loadData: 如果页面加载过程需要调用MobileAPI,则写在这个地方。

我们在程序里把代码分门别类写在各自的地方,易于管理(避免了经常会声明了变量而忘记销毁的问题)。

于是刚才的代码文件,我们将其重构为:

 
 1 #import "APageViewController.h"
2
3 @interface APageViewController () {
4 UILabel* nameLabel;
5 UILabel* ageLabel;
6 UIButton* getInfoButton;
7 UIButton* clearInfoButton;
8 }
9
10 @end
11
12 @implementation APageViewController
13
14 - (void)createFields {
15
16 }
17
18 - (void)destroyFields {
19
20 }
21
22 - (void)createViews {
23 //1.从xib中获取View
24 NSArray* list = [[NSBundle mainBundle] loadNibNamed: @"APageView" owner: self options: nil];
25 self.view = list.lastObject;
26
27 nameLabel = (UILabel*)[self.view viewWithTag: 100];
28 nameLabel.text = @"";
29
30 ageLabel = (UILabel*)[self.view viewWithTag: 200];
31 ageLabel.text = @"";
32
33 getInfoButton = (UIButton*)[self.view viewWithTag: 300];
34 clearInfoButton = (UIButton*)[self.view viewWithTag: 400];
35 }
36
37 - (void)destroyViews {
38
39 }
40
41 - (void)createEvents {
42 [getInfoButton addTarget: self action: @selector(getInfo) forControlEvents:UIControlEventTouchUpInside];
43 [clearInfoButton addTarget: self action: @selector(clearInfo) forControlEvents:UIControlEventTouchUpInside];
44
45 }
46
47 - (void)destroyEvents {
48
49 }
50
51 - (void)loadData {
52 //在这里调用API,对于多个API的调用,参加后续章节
53 }
54
55 - (void) getInfo {
56 nameLabel.text = @"包小强";
57 ageLabel.text = @"31.6";
58 }
59
60 - (void) clearInfo {
61 nameLabel.text = @"";
62 ageLabel.text = @"";
63 }
64
65 @end
 

以上的代码重构,要遵守几个规则:

1)在createFields方法中接收从上一个页面传递过来的参数

2)在createFields方法中初始化变量

3)将要操作的控件,都在ViewController中作为类级别的变量来声明

3)在createViews方法中,加载xib文件,并通过Tag给控件一次性赋值

4)在createEvent方法中,为控件挂上事件方法,比如按钮的点击

5)如果有NotificationCenter,统一在createEvent方法中addObserver,在destroyEvent方法中removeObserver。

6)在DestroyFields方法中,释放/销毁所有引用型变量。

7)在DestroyViews方法中,释放/销毁所有控件。

所有的ViewController都这么写,整个App整齐划一。尤其是将一个页面的所有控件一次性都从xib中根据tag值取出来,虽然浪费了一些内存,但是可以随时随地直接使用。

将声明一个按钮和为按钮添加一个点击事件方案分开在2个方法内写,一开始你会非常不习惯,但是当控件多了、事件多了的时候,是一目了然的。记住,我们在做的是企业级App开发,而不是小型App。

看到最后,熟悉网站端编程的人笑了,没错,这种新的生命周期,就是从javascript中借鉴来的。js是一门弱语言,所以需要自定义生命周期并按部就班在不同的方法中写不同的方法,生命周期的重新定义,或者说是扩展,只是js代码框架中的一个小部分。

本章代码下载:

YoungHeart-Chapter-04-1.zip (重构前)

YoungHeart-Chapter-04-2.zip   (重构后)

 
 
分类: App开发

自定义生命周期的设计(iOS篇)的更多相关文章

  1. 无线客户端框架设计(4):自定义生命周期的设计(iOS篇)

    首先要确定一点,我们的App,要基于XIB文件进行编程,而不是在每个相应的ViewController里面去手动创建页面的每个控件.这样做的好处是,将页面布局与业务逻辑彻底隔离.于是我们可以把xib的 ...

  2. API生命周期第二阶段——设计:采用swagger进行API描述、设计

    本篇博客主要是以swagger为依托,介绍API生命周期的第二个阶段--设计!在详细介绍之前,我必须声明一点:如果是想了解swagger和项目框架的集成的,这里没有.我要介绍的swagger进行的AP ...

  3. 对微信小程序的生命周期进行扩展 – Typescript 篇

    最近利用业余时间倒腾了一个微信小程序,主要目的是横向比较一些业界小程序平台的架构和做法.因为有在其他平台长期的开发经验,对于小程序的一些机制做了一些辩证的思考.例如,小程序的页面,其实不是一个页面,而 ...

  4. 连载《一个程序猿的生命周期》-《发展篇》 - 7.是什么阻碍了"程序猿"的发展?

    有两件事想记录一下,具有普遍性和代表性."程序猿"加了引号,是泛指一类人,也并非局限于IT行业.       山东子公司的总经理是公司大股东之一,个子不高.有些秃顶.面容显老,但看 ...

  5. 连载《一个程序猿的生命周期》-《发展篇》- 22.城市奋斗者的阿Q精神

    好久没有写文章了,有些人会认为博主肯定是没有什么好写的了.其实不然,是想写的太多,实在是没有时间.上半年一直比较忙,处于加班常态的状态,身心疲惫.相较于城市的伪奋斗者,我算比较实干的,而界定“实干”的 ...

  6. 连载《一个程序猿的生命周期》-《发展篇》 - 3.农民与软件工程师,农业与IT业

    相关文章:随笔<一个程序猿的生命周期>- 逆潮流而动的“叛逆者”        15年前,依稀记得走出大山,进城求学的场景.尽管一路有父亲的陪伴,但是内心仍然畏惧.当父亲转身离去.准备回到 ...

  7. QQ聊天界面的布局和设计(IOS篇)-第一季

    我写的源文件整个工程会再第二季中发上来~,存在百度网盘, 感兴趣的童鞋, 可以关注我的博客更新,到时自己去下载~.喵~~~ QQChat Layout - 第一季 一.准备工作 1.将假数据messa ...

  8. API生命周期第二阶段——设计:如何设计API(基于swagger进行说明)

    题外话 在新的项目中,推行了swagger用于对API的设计.以期待解决我们上篇博客中说到了一些现象,提升工作效率.那么,结合到当时和全项目组成员做培训,以及后续的给主要应用者做培训,以及当初自己接触 ...

  9. ASP.NET页面生命周期总结(完结篇)

    补充: W3svc服务  负责把‘工作进程’启动起来 W3svc 连接工作进程.内核模块.IIS 主服务的一个核心的桥梁 W3svc还有一个作用就是维护应用程序池,可以设置多长时间回收,多长时间重启. ...

随机推荐

  1. C语言星号的秘密

    C语言星号的秘密 星号的秘密 1.乘法运算符   2.定义指针 int *p = 0; 还是 int* p = 0;? 后一种比较容易这样理解:定义了一个变量p,它是指针型的(更详细一点,是指向int ...

  2. NOIP 2012 解决问题的方法

    [D1T1vigenerepassword] P1778vigenerepassword Accepted 标签:[显示标签] 描写叙述 16世纪法国外交家Blaise de Vigenère设计了一 ...

  3. js 利用iframe和location.hash跨域解决的方法,java图片上传回调JS函数跨域

    奶奶的:折腾了我二天,最终攻克了!网上有非常多样例. 但跟我的都不太一样,费话不多说了,上图   上代码: IE ,firefix,chrome 測试通过 js :这个主页面,部分代码, functi ...

  4. JS里写入(混写)php asp

    原文:JS里写入(混写)php asp JS里写入(混写)php 方法1:<Br> <script language="javascript"> docum ...

  5. POJ 3373 Changing Digits 好蛋疼的DP

    一開始写的高位往低位递推,发现这样有些时候保证不了第四条要求.于是又開始写高位往低位的记忆化搜索,又发现传參什么的蛋疼的要死.然后又发现高位開始的记忆化搜索就是从低位往高位的递推呀,遂过之. dp[i ...

  6. Android NDK的C++11标准支持

    C++11于Android NDK它已被支持,本文介绍了如何NDK添加C++11支持标准. 在开源项目Cocos2d-x于,他已经加入C++11支持标准. 1.改动Application.mk文件,加 ...

  7. json.net 比jsonIgnore 更好的方法 修改源码

    关于 JsonIgnore  问题, EF T4 模板 中 存在主外键关系 namespace WindowsFormsApplication1{    using System;    using ...

  8. 在线预览Excel

    遇到的问题各种多 <system.web>        <identity impersonate="true" userName="Administ ...

  9. Socket 学习(二)

    1对1的发送和接收 Client 端         void ThreadReceive()         {             try             {              ...

  10. javascritpt 原型链

    // 基类 var BaseCalculator = function(){ this.decimalDigits = 2; }; // public BaseCalculator.prototype ...