无线客户端框架设计(4):自定义生命周期的设计(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文件,如下所示:
接下来要做的事情,有时候连我都觉得匪夷所思。我们先来看一段代码:
#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篇)的更多相关文章
- 自定义生命周期的设计(iOS篇)
自定义生命周期的设计(iOS篇) 首先要确定一点,我们的App,要基于XIB文件进行编程,而不是在每个相应的ViewController里面去手动创建页面的每个控件.这样做的好处是,将页面布局与业务逻 ...
- Newbe.Claptrap 框架如何实现多级生命周期控制?
Newbe.Claptrap 框架如何实现多级生命周期控制?最近整理了一下项目的术语表.今天就谈谈什么是 Claptrap Lifetime Scope. 特别感谢 kotone 为本文提供的校对建议 ...
- 无线客户端框架设计(5.1):将JSON映射为实体对象(iOS篇)
iOS开发人员已经习惯于将JSON转换为字典或者数组来进行操作了,接下来我要做的事情,可能匪夷所思,但是,对WP和Android开发人员而言,他们更倾向于将JSON转换为实体对象进行操作. 我所设计的 ...
- 无线客户端框架设计(3):基类的设计(iOS篇)
本文代码:YoungHeart-Chapter-03.zip 没有基类的App都不是好App. 因为iOS使用的是mvc模式的开发模式,所以,业务逻辑基本都在每个页面相应的ViewController ...
- API生命周期第二阶段——设计:采用swagger进行API描述、设计
本篇博客主要是以swagger为依托,介绍API生命周期的第二个阶段--设计!在详细介绍之前,我必须声明一点:如果是想了解swagger和项目框架的集成的,这里没有.我要介绍的swagger进行的AP ...
- Django 框架 django的请求生命周期
概述 首先我们知道HTTP请求及服务端响应中传输的所有数据都是字符串,同时http请求是无状态的,可以通过session和cookie来辅助. 浏览器通过ip和端口及路由方式访问服务端. 在Djang ...
- 浅读tomcat架构设计之tomcat生命周期(2)
浅读tomcat架构设计和tomcat启动过程(1) https://www.cnblogs.com/piaomiaohongchen/p/14977272.html tomcat通过org.apac ...
- spring框架中Bean的生命周期
一.Bean 的完整生命周期 在传统的Java应用中,bean的生命周期很简单,使用Java关键字 new 进行Bean 的实例化,然后该Bean 就能够使用了.一旦bean不再被使用,则由Java自 ...
- Omi框架学习之旅 - 生命周期 及原理说明
生命周期 name avatars company constructor 构造函数 new的时候 install 初始化安装,这可以拿到用户传进的data进行处理 实例化 installed 安装完 ...
随机推荐
- 英语语法 It all started the summer before second grade when our moving van pulled into her neighborhood
It all started the summer before second grade when our moving van pulled into herneighborhood It all ...
- Ubuntu 10.04 32位桌面版+OpnERP 6.1.1
1.准备环境: sudo apt-get install denyhosts sudo apt-get update sudo apt-get dist-upgrade sudo adduser ...
- Great StackOverflow questions
1. diffenece between MVC and MVP http://stackoverflow.com/questions/2056/what-are-mvp-and-mvc-and-wh ...
- [2015hdu多校联赛补题]hdu5297 Y sequence
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5297 题意:给你一个所有正整数的序列,然后去掉满足x^(2~r)的所有数(x为所有正整数,r>= ...
- python第十一天-----补:线程池
低版本: #!/usr/bin/env python import threading import time import queue class TreadPool: ""&q ...
- android之RadioGroup
radioGroup这控件在开发中也是常用到的 RadioGroup 有时候比较有用.主要特征是给用户提供多选一机制. 用微信举一个例子吧! <?xml version="1.0&qu ...
- redis在windows中的安装
之前介绍过了redis的相关知识,以及在linux平台上安装redis,为了方便学习,这里记录一下redis在windows平台上的安装步骤 1.下载redis下载地址https://github.c ...
- CXF 入门:创建一个基于WS-Security标准的安全验证(CXF回调函数使用,)
http://jyao.iteye.com/blog/1346547 注意:以下客户端调用代码中获取服务端ws实例,都是通过CXF 入门: 远程接口调用方式实现 直入正题! 以下是服务端配置 ==== ...
- LeetCode(五)
Minimum Depth of Binary Tree public class Solution { public int minDepth(TreeNode root) { if(root==n ...
- try-catch-finally的含有return使用揭秘
很多人都会纠结这么一个问题try-catch-finally中有return的情况,我自己总结如下: 如果是值类型的话 请看代码 using System; using System.Collecti ...