首先要确定一点,我们的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文件,如下所示:

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

 #import "APageViewController.h"

 @interface APageViewController ()

 @end

 @implementation APageViewController

 - (void)loadView
{
[super loadView];
// Do any additional setup after loading the view. //1.从xib中获取View
NSArray* list = [[NSBundle mainBundle] loadNibNamed: @"APageView" owner: self options: nil];
self.view = list.lastObject; UILabel* nameLabel = (UILabel*)[self.view viewWithTag: ];
nameLabel.text = @""; UILabel* ageLabel = (UILabel*)[self.view viewWithTag: ];
ageLabel.text = @""; UIButton* getInfoButton = (UIButton*)[self.view viewWithTag: ];
[getInfoButton addTarget: self action: @selector(getInfo) forControlEvents:UIControlEventTouchUpInside]; UIButton* clearInfoButton = (UIButton*)[self.view viewWithTag: ];
[clearInfoButton addTarget: self action: @selector(clearInfo) forControlEvents:UIControlEventTouchUpInside];
} - (void) getInfo {
UILabel* nameLabel = (UILabel*)[self.view viewWithTag: ];
nameLabel.text = @"包小强"; UILabel* ageLabel = (UILabel*)[self.view viewWithTag: ];
ageLabel.text = @"31.6";
} - (void) clearInfo {
UILabel* nameLabel = (UILabel*)[self.view viewWithTag: ];
nameLabel.text = @""; UILabel* ageLabel = (UILabel*)[self.view viewWithTag: ];
ageLabel.text = @"";
} - (void)dealloc {
[super dealloc];
} @end

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

我们发现,有2个问题:

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

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

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

Normal
0

10 pt
0
2

false
false
false

EN-US
ZH-CN
X-NONE

$([{£¥·‘“〈《「『【〔〖〝﹙﹛﹝$(.[{£¥
!%),.:;>?]}¢¨°·ˇˉ―‖’”…‰′″›℃∶、。〃〉》」』】〕〗〞︶︺︾﹀﹄﹚﹜﹞!"%'),.:;?]`|}~¢

/* Style Definitions */
table.MsoNormalTable
{mso-style-name:普通表格;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.5pt;
mso-bidi-font-size:11.0pt;
font-family:Calibri;
mso-ascii-font-family:Calibri;
mso-ascii-theme-font:minor-latin;
mso-hansi-font-family:Calibri;
mso-hansi-theme-font:minor-latin;
mso-font-kerning:1.0pt;}

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

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

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

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

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

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

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

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

 #import "APageViewController.h"

 @interface APageViewController () {
UILabel* nameLabel;
UILabel* ageLabel;
UIButton* getInfoButton;
UIButton* clearInfoButton;
} @end @implementation APageViewController - (void)createFields { } - (void)destroyFields { } - (void)createViews {
//1.从xib中获取View
NSArray* list = [[NSBundle mainBundle] loadNibNamed: @"APageView" owner: self options: nil];
self.view = list.lastObject; nameLabel = (UILabel*)[self.view viewWithTag: ];
nameLabel.text = @""; ageLabel = (UILabel*)[self.view viewWithTag: ];
ageLabel.text = @""; getInfoButton = (UIButton*)[self.view viewWithTag: ];
clearInfoButton = (UIButton*)[self.view viewWithTag: ];
} - (void)destroyViews { } - (void)createEvents {
[getInfoButton addTarget: self action: @selector(getInfo) forControlEvents:UIControlEventTouchUpInside];
[clearInfoButton addTarget: self action: @selector(clearInfo) forControlEvents:UIControlEventTouchUpInside]; } - (void)destroyEvents { } - (void)loadData {
//在这里调用API,对于多个API的调用,参加后续章节
} - (void) getInfo {
nameLabel.text = @"包小强";
ageLabel.text = @"31.6";
} - (void) clearInfo {
nameLabel.text = @"";
ageLabel.text = @"";
} @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   (重构后)

无线客户端框架设计(4):自定义生命周期的设计(iOS篇)的更多相关文章

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

    自定义生命周期的设计(iOS篇) 首先要确定一点,我们的App,要基于XIB文件进行编程,而不是在每个相应的ViewController里面去手动创建页面的每个控件.这样做的好处是,将页面布局与业务逻 ...

  2. Newbe.Claptrap 框架如何实现多级生命周期控制?

    Newbe.Claptrap 框架如何实现多级生命周期控制?最近整理了一下项目的术语表.今天就谈谈什么是 Claptrap Lifetime Scope. 特别感谢 kotone 为本文提供的校对建议 ...

  3. 无线客户端框架设计(5.1):将JSON映射为实体对象(iOS篇)

    iOS开发人员已经习惯于将JSON转换为字典或者数组来进行操作了,接下来我要做的事情,可能匪夷所思,但是,对WP和Android开发人员而言,他们更倾向于将JSON转换为实体对象进行操作. 我所设计的 ...

  4. 无线客户端框架设计(3):基类的设计(iOS篇)

    本文代码:YoungHeart-Chapter-03.zip 没有基类的App都不是好App. 因为iOS使用的是mvc模式的开发模式,所以,业务逻辑基本都在每个页面相应的ViewController ...

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

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

  6. Django 框架 django的请求生命周期

    概述 首先我们知道HTTP请求及服务端响应中传输的所有数据都是字符串,同时http请求是无状态的,可以通过session和cookie来辅助. 浏览器通过ip和端口及路由方式访问服务端. 在Djang ...

  7. 浅读tomcat架构设计之tomcat生命周期(2)

    浅读tomcat架构设计和tomcat启动过程(1) https://www.cnblogs.com/piaomiaohongchen/p/14977272.html tomcat通过org.apac ...

  8. spring框架中Bean的生命周期

    一.Bean 的完整生命周期 在传统的Java应用中,bean的生命周期很简单,使用Java关键字 new 进行Bean 的实例化,然后该Bean 就能够使用了.一旦bean不再被使用,则由Java自 ...

  9. Omi框架学习之旅 - 生命周期 及原理说明

    生命周期 name avatars company constructor 构造函数 new的时候 install 初始化安装,这可以拿到用户传进的data进行处理 实例化 installed 安装完 ...

随机推荐

  1. android 学习(1)

    第二次发生这种事了,写完啦,手一抖,丢了,这个写完不用博客园了,怪不得用户越来越少 简写 androidstudio安装注意点 1JDK安装需配置环境变量 2安装遇到haxm错误需要进入BIOS打开C ...

  2. redis3.0.5集群部署安装详细步骤

    Redis集群部署文档(centos6系统) (要让集群正常工作至少需要3个主节点,在这里我们要创建6个redis节点,其中三个为主节点,三个为从节点,对应的redis节点的ip和端口对应关系如下) ...

  3. nullcon HackIM 2016 -- Programming Question 2

    Your simple good Deeds can save you but your GREED can kill you. This has happened before. This gree ...

  4. 错误:创建 cachingConfiguration 的配置节处理程序时出错: 未能加载文件或程序集“Microsoft.Practices.EnterpriseLibrary.Caching,

    问题: 错误:创建 cachingConfiguration 的配置节处理程序时出错: 未能加载文件或程序集“Microsoft.Practices.EnterpriseLibrary.Caching ...

  5. 2014 ACM/ICPC 北京邀请赛 部分 题解

    题目链接:http://acm.bnu.edu.cn/bnuoj/problem.php?search=2014+ACM-ICPC+Beijing+Invitational+Programming+C ...

  6. [原创][LaTex]LaTex学习笔记之框架及宏包

    0. 简介 LaTex在书写文档时的最基本单元就是首部的写作,变相的也可以说是头文件.本文章就来总结一下文档的基本格式和常用宏包. 1. 基本单元 基本单元需要对LaTex语法有一定的了解,这个很简单 ...

  7. golang--gopher北京大会(1)

    大会感想:牛人真的很能写代码,实现很多功能,而且开源的精品越多,影响力越大,越能过上dream life.比如beego的作者,去了America,进入了Apple.另外,精英们特点是表达能力很强,也 ...

  8. 如何使用JS脚本从HTML中分离图片标签与文本,替换文本中指定的内容并加粗(原创)

    var html='ddfsdfsdfdsd dfsdfsdffds<img _src="http://localhost:8490/60E86EA7-FE7B-44BF-8270-4 ...

  9. C#的UDP服务器

    最新优化版本 /* http://www.cnblogs.com/zengqinglei/archive/2013/04/27/3046119.html */ using System; using ...

  10. C++主要数据类型在计算机中所占字节大小

    遇到了数据存储的大端和小端问题,这你妹的看的一头雾水,发现我基本知识严重匮乏啊,先了解C++各数据类型在自己机子上占多少字节吧,以及这些数据类型所占字节大小与神马有关.各种查资料然后写代码检验,小结于 ...