阅读:12968次   评论:0条   更新时间:2011-06-23    




 
 书名:iPad应用开发实战

 作者:李晨

 定价:55.00元

 出版时间:2011年4月

 当当网:http://product.dangdang.com/product.aspx?product_id=21060576&ref=search-1-pub

 互动网:http://product.china-pub.com/197991

 卓越网:  http://www.amazon.cn/dp/B004WZ9O3I

 豆瓣网:http://book.douban.com/subject/6069034/

 

内容简介

     《iPad应用开发实战》由资深iOS应用开发专家(现就职于美国苹果公司)亲自撰写,Lordhong和沈卓立等资深iOS/Android应用开发专家联袂推荐,国内著名的Apple开发者社区CocoaChina联合策划,权威性毋庸置疑。

《iPad应用开发实战》内容系统而全面,详尽地讲解了iPad开发相关的知识,所有新功能和新特性一览无余;实战性强,不仅配有大量精心设计的小案例,而且还有一个贯穿全书的大案例。本书不仅能满足你全面而系统地学习iPad开发理论知识的需求,还能满足你需要充分实践的需求。

      全书一共分为18章,首先介绍了开发iPad应用必备的常识和工具,并给出了一个简单的示例;然后详细地讲解了内存管理、视图和视图控制器、键盘输入和自定义键盘、绘图、触摸事件与手势识别、动画、多媒体播放、容器类、文件系统、同时性编程与定时器、cocos2d基础、连接设备、加速度传感器、地图与链接服务器、Objective-C与XCode进阶知识等核心内容;最后介绍了如何申请iPad应用的开发证书,以及以作者的亲身经历和经验讲述了如何更好地营销和推广自己的应用。

      无论你是有丰富经验的iPhone应用开发者,还是略有经验的iPad应用开发者,抑或是只有Objective-C基础但毫无经验的开发者,本书都非常适合你,它是为所有iPad应用开发经验还不太丰富的读者准备的。

 

作者简介

      李晨,资深iOS开发工程师,现就任于苹果(美国)公司,负责iPhone/iPad相关应用的开发,经验极其丰富。在iPhone发布初期即看好移动应用开发的前景,随即转向iOS开发平台,并独立发布了多款热销的iPhone应用,在APP Store上创下佳绩。随后,在iPad面世的第一时间内又开发了iPad相关的应用,而且也取得了不错的销售成绩,积累了大量实战经验。从学生时代开始,他就一直在为Boston Scientific和Vobile等世界知名的企业做开发,项目涉及医疗仪器和移动广告平台等领域。他亲手创办了密歇根理工大学iPhone开发俱乐部,将很多学生带入iOS开发的阵营。此外,他在核工程、无线通信和信号处理等领域也有较为深入的研究。

《iPad应用开发实战》前言 Top

前言

    欢迎加入iPad应用开发者的阵营。
    如果你已经是一名iPhone应用或者Mac应用的开发者,希望转战iPad应用开发,那么本书能帮助你最快地掌握iPad特有的设计思想和API。如果你没有任何iOS或者Mac OS开发经验,希望从零开始学习iPad应用开发,那么本书对Objective-C语言的介绍以及大量的开发实例将帮助你从理论和实践两个方面迅速入门。
    2007年,苹果公司发布了第一代iPhone,立即引发了智能手机产业的革命。
    2008年,苹果公司发布了iPhone SDK,允许开发者为iPhone开发应用,并通过App Store进行推广和销售,这引发了移动软件开发的狂潮。
    2009年,这是百花齐放的一年。大量的个人开发者、小公司、大公司在开发、销售等领域各显神通,发布了大量的iPhone应用,很多开发者也从中收获颇丰。
    2010年,苹果公司发布了iPad,又一次引发了业界的革命—这一次是在平板电脑领域。iPad不仅为市场注入了新的活力,也为开发者带来了新的梦想和新的机遇。
    本书就是为这些胸怀梦想、正在寻找机遇的人而写的。它从市场环境、应用内容、软件构架等多方面介绍了iPad的开发思想,从技术的角度介绍了iPad的开发细节,并且通过大量实例诠释如何将iPad应用从构想变成现实,从计划变成收入。

 

本书内容特色
     本书中介绍的所有应用均使用Objective-C语言开发实现。Objective-C是iPad、iPhone、Mac应用开发中的主要语言。
     从语法特性、常用类、常用方法等多角度介绍Objective-C语言。
     注重iPad特性,强调其与iPhone的不同之处,这样帮助读者从应用设计上更好地贴近iPad。
     大量的实例,涉及图像处理、娱乐、游戏、文字处理等多个领域。有简单的例子,用于解释简单概念,适合初学者;有复杂的例子,覆盖多个章节,适合进阶读者。
     介绍了最为实用的控件和API,例如列表视图、分割视图、弹出视图、加速度传感器等,帮助读者掌握在短时间内发布应用所需要的基本技能。
     开发环境的介绍,帮助读者熟练掌握XCode的开发、调试、快捷键功能,使开发效率成倍提高。
     关键词中英文对照,让读者能够用母语来理解关键词和概念,也方便读者查询英文资料。
     对iOS系统和应用生命周期的概述,让开发者能够对应用所运行的环境以及所经历的各个阶段有更深入的了解,从而在应用设计上能够有深思熟虑的计划。
     强调移动开发和桌面开发的不同之处,尤其是在内存管理和异步方法上,从而帮助读者开发出更适合移动设备的应用,而不是简单的桌面软件的“山寨版”。
     对App Store市场历史以及市场环境的介绍,让开发者在设计应用时能更加贴近市场,贴近用户。
     介绍了很多实用的营销技巧,帮助开发者更好地宣传和销售自己的应用,获得更多的收益。

 

适合的读者与阅读方法
     有iOS或Mac OS编程经验的开发者:应当阅读本书的每一章,但可以跳过一些Objective-C的语法讲解部分,而专注于学习iPad特有的设计思想和API。同时可以利用大量的实例,迅速进入iPad开发的最佳状态。
     有C++、Java、QT等面向对象编程经验的开发者:应当阅读本书的每一章,并特别注意Objective-C语法。Objective-C的语法有很多独特之处,内存管理是其中的难点和重点,需要多花时间。另外,移动应用的架构和桌面软件也很不相同,需要注意相关知识点。
     没有任何编程经验的初学者:应当仔细阅读本书每一章,从简单的例子入手,在例子的基础上进行改动和试验,了解每一处代码的用途;然后开始“临摹”创作,写一个小小的应用,掌握应用的基本架构;之后再继续学习语法、API,循序渐进,掌握iPad开发的基本技能。
     软件策划与营销人员:可以蜻蜓点水地浏览第2~17章,了解iPad应用可能具有的功能以及硬件性能;然后多多试玩App Store上的优秀应用,学习优秀和新颖的观点;最后应当阅读第1章和第18章,从中了解App Store的历史与现状,以及一些营销策略。
     创业者:作为一名创业者,需要有较为全面的技能,但也许没有太多时间详细学习每一章。因此,可以略读各个章节,从中得到创意和技术上的启发。另外,应当熟悉每一个应用实例的构架和功能。
源代码下载
    为了便于大家实践,本书提供了源代码供大家下载,下面两种下载途径均有效:
     通过华章公司的官方网站(www.hzbook.com)下载,在网站上找到该书的页面即可。
     通过我的博客(www.lichen1985.com/iPadCode.html)下载。

 

联系作者
    如果你发现了本书的任何问题或有任何建议给我们,欢迎通过下面的邮件地址与我联系:iPadAppPracticalProgramming@gmail.com

 

致谢
    本书的编写,经历了撰写、审核、修改、复审、再修改、编辑等很多很多轮的改进。这本书的出版是很多人共同努力的结果。
    我首先要感谢华章公司,是公司里每一位老师、编辑的辛勤劳动,使得稿件的质量不断提升,并最终得以出版。其中尤其要感谢杨福川编辑,他一直在坚定地支持着我写作以及相关的一切工作。
    我要感谢Cocoa China论坛、密歇根理工大学iPhone开发俱乐部、苹果开发论坛(Apple Developer Forum)以及其他很多iOS开发社区。和众多开发者共同学习、讨论使我受益匪浅,并且在iOS开发的道路上走得越来越远。
    我要感谢我的家人和朋友,他们在我写作最为艰苦的时候给我以鼓励,让我坚持下来,并将书稿质量不断提高。
    最后,我要感谢苹果公司!这家传奇的公司在2010年发布的iPad为世界带来了全新的移动体验,也为开发者们带来了新的梦想。我还要感谢众多的用户与批评者,他们购买并使用我的应用,不断地给我提出批评和建议,使得我的应用越做越好。

 

《iPad应用开发实战》目录 Top

 

前言

第1章   iPad开发入门 / 1

1.1   iPad概览 / 2

1.2   从PC到iPad / 3

1.3   从iPhone到iPad / 4

1.4   iPad开发的必备知识 / 5

1.5   iPad开发的必备工具 / 5

1.6   第一个iPad应用—Hello World / 6

1.6.1   建立工程:HelloWorldMail / 7

1.6.2   工程概览 / 8

1.6.3   添加按钮 / 15

1.6.4   邮件编辑器 / 17

1.7   小结 / 19

第2章   视图和视图控制器 / 20

2.1   视图(UIView) / 21

2.1.1   视图概览 / 21

2.1.2   窗口(UIWindow) / 22

2.1.3   网页视图(UIWebView) / 23

2.2   视图控制器(UIViewController) / 23

2.2.1   视图控制器概览 / 23

2.2.2   导航控制器(UINavigationController) / 24

2.2.3   标签控制器(UITabBarController) / 25

2.3   弹出控制器(UIPopoverController) / 26

2.4   分割视图控制器(UISplitViewController) / 27

2.5   实例:iPad网页导航 / 27

2.5.1   基于分割视图控制器的应用 / 27

2.5.2   网页视图 / 29

2.5.3   表格视图 / 31

2.6   小结 / 34

第3章   内存管理 / 35

3.1   对象所有权、引用计数与释放 / 36

3.2   自动释放与便捷方法 / 38

3.3   访问器方法与属性 / 39

3.4   改变引用计数的特殊情况 / 40

3.5   内存管理总结 / 41

第4章   键盘输入和自定义键盘 / 42

4.1   文本输入与系统键盘 / 43

4.2   自定义键盘之一:替代系统键盘 / 47

4.3   自定义键盘之二:与系统键盘共存 / 50

4.4   自定义键盘之三:修改系统键盘 / 51

4.5   小结 / 55

第5章   绘图 / 56

5.1   iOS概览 / 57

5.2   iOS中的绘图技术 / 58

5.3   Quartz 2D / 59

5.4   贝济埃路径的使用 / 61

5.5   画图实战:绘图板 / 62

5.6   小结 / 74

第6章   容器类的使用 / 76

6.1   数组(NSArray)与可变数组(NSMutableArray) / 77

6.1.1   数组的创建与初始化 / 78

6.1.2   查询数组 / 78

6.1.3   产生新数组 / 79

6.1.4   比较数组 / 79

6.1.5   保存数组 / 79

6.1.6   添加对象(NSMutableArray方法) / 79

6.1.7   移除对象(NSMutableArray方法) / 79

6.1.8   替换对象 / 80

6.2   字典(NSDictionary)与可变字典(NSMutableDictionary) / 80

6.2.1   创建字典 / 80

6.2.2   获取关键字与数值 / 81

6.2.3   保存字典 / 81

6.2.4   添加条目(NSMutableDictionary方法) / 81

6.2.5   移除条目(NSMutableDictionary方法) / 81

6.3   集合(NSSet)与可变集合(NSMutableSet) / 81

6.3.1   创建集合 / 82

6.3.2   读取集合 / 82

6.3.3   比较集合 / 82

6.3.4   添加或移除对象(NSMutableSet方法) / 82

6.3.5   集合操作(NSMutableSet方法) / 83

6.4   容器在拼图游戏中的使用 / 83

6.4.1   拼图游戏的设计 / 83

6.4.2   相关定义 / 84

6.4.3   Piece类与字典 / 87

6.4.4   Puzzle类与字典和数组 / 89

6.5   小结 / 93

第7章   文件系统 / 94

7.1   iOS文件系统概述 / 95

7.2   通过模拟器看应用的包裹 / 96

7.3   文件管理器 / 97

7.4   NSString的路径功能 / 98

7.5   拼图游戏中的文件管理 / 99

7.6   小结 / 107

第8章   绘图进阶 / 108

8.1   屏幕旋转 / 109

8.2   绘制曲线和切割图案 / 111

8.3   高级绘图在拼图游戏中的应用 / 114

8.4   小结 / 122

第9章   触摸事件与手势识别 / 123

9.1   触摸事件、视图结构与响应顺序 / 124

9.2   官方SDK中的手势识别器 / 126

9.3   Photo Jigsaw中对触摸的响应 / 128

9.4   自定义的手势识别器 / 135

9.5   小结 / 136

第10章   同时性编程与定时器 / 137

10.1   同时性编程概述 / 138

10.2   操作队列 / 140

10.3   拼图游戏中的同时性编程 / 142

10.4   定时器及其在拼图游戏中的使用 / 146

10.5   小结 / 148

第11章   动画 / 150

11.1   iOS动画技术概述 / 151

11.2   用图像视图来逐帧显示动画 / 153

11.3   视图的动画技术 / 154

11.4   CogRadio应用 / 156

11.5   CogRadio应用中的动画 / 160

11.6   小结 / 166

第12章   多媒体播放 / 167

12.1   音频播放 / 168

12.1.1   使用系统声音服务来播放音效 / 168

12.1.2   音频播放器 / 169

12.1.3   音乐播放器 / 169

12.2   视频播放 / 171

12.2.1   电影播放器 / 171

12.2.2   在网页视图中嵌入播放 / 174

12.3   CogRadio中的多媒体 / 175

12.4   小结 / 178

第13章   cocos2d开发入门 / 179

13.1   cocos2d简介 / 181

13.1.1   导演 / 181

13.1.2   场景和层 / 182

13.1.3   精灵与动作 / 183

13.2   创建cocos2d工程并添加场景 / 184

13.3   添加按钮、精灵和动作 / 185

13.4   粒子系统 / 187

13.5   小结 / 193

第14章   连接设备 / 195

14.1   游戏工具箱简介 / 196

14.1.1   游戏中心 / 196

14.1.2   点对点连接 / 198

14.1.3   语音聊天 / 198

14.2   实现点对点连接 / 199

14.3   OpenFeint 游戏平台 / 203

14.4   小结 / 209

第15章   加速度传感器 / 210

15.1   加速度传感器概述 / 211

15.2   飞机躲子弹游戏 / 212

15.3   倾斜设备来控制飞机 / 216

15.4   分离重力成分 / 219

15.5   小结 / 221

第16章   地图与连接服务器 / 222

16.1   地图编程概述 / 223

16.2   实战:地图编程 / 224

16.3   与服务器交互 / 227

16.4   实战:与服务器交互 / 229

16.5   小结 / 235

第17章   Objective-C与XCode进阶 / 236

17.1   NSLog扩展 / 237

17.2   范畴的使用 / 239

17.3   读写器的内存管理 / 240

17.4   KVO / 242

17.5   单例 / 243

17.6   运行时的对象查询 / 245

17.7   调试器的巧用 / 246

17.8   XCode快捷键 / 247

17.9   小结 / 249

第18章   iPad应用的开发证书与营销 / 250

18.1   开发证书的获取 / 251

18.2   联机调试 / 254

18.3   应用名字、说明以及图片 / 258

18.4   应用的提交 / 261

18.5   应用的推广 / 262

18.6   应用升级与销售技巧 / 263

18.7   iPad开发的长期策略 / 265

18.8   小结 / 266

第1章 iPad开发入门 Top

第1章
iPad开发入门
1.1   iPad概览
1.2   从PC到iPad
1.3   从iPhone到iPad
1.4   iPad开发的必备知识
1.5   iPad开发的必备工具
1.6   第一个iPad应用—Hello World
1.7   小结
    无论你是一个熟练的iPhone开发者,还是一个从未接触过iOS(iPhone Operation System)开发的程序员,无论你是把iPad开发当成一种职业,还是把它作为业余淘金的手段,在你准备开始写代码之前,你首先要做的就是拿起这款设备,下载几款最畅销的iPad应用,感受它们与设备的完美结合以及与用户的良好交互—是的,你的目标就是要开发出那样的应用。这本书所要做的,就是为你提供达成这个目标所需的全部基础知识。这一章首先帮助你熟悉iPad。
    首先需要了解iPad和PC的关键区别。iPad是一款移动设备,它们有着与PC不同的硬件,基于不同的操作系统,运行着不同的软件,用户交互方式也有着天壤之别。这是设计iPad应用的基础。
    那么iPad和iPhone相比有何差别呢?它们同为移动设备,同样运行着iOS,iPad的高分辨率和大屏幕就成了两者的决定性差异。如果说iPhone为你提供了一个可以实现梦想的世界,那么在iPad上,你应当寻找更大的梦想。
对iPad有了基本的认识以后,本章将简单介绍iPad开发的必备知识和工具,这些是赢得iPad用户的武器。有了这些武器之后,你需要一个漂亮的登场秀:遵照软件教程的传统,你将实现一个在iPad上运行的Hello World程序。这个程序能够让你通过电子邮件对朋友们说“Hello World!”。
   
注意  在这一章中,我们专注的是iPad本身,而非代码—因为做开发不仅仅是写代码。写代码时,需要的是理解编程语言并熟悉API,然后实现别人的软件设计;而作为一名开发者,编程技能只是基础,更重要的是理解用户需求,了解设备潜能,然后设计并实现出杀手级的应用。同样地,在本书中,我们将始终强调对iPad元素的理解,然后用代码来帮助理解和实现这些元素。
下面,可以开始你的iPad之旅了!

1.1 iPad概览 Top

    关于iPad,有一些参数是首先需要了解的,包括它的重量、大小、屏幕尺寸、容量、操作系统特性、通信接口、传感器等。有了这些知识,才能知道这款设备的潜能,以及设计的应用所能包含的功能。下面所介绍的参数以第一代iPad的Wi-Fi型号为例。
    iPad高为24.28厘米,宽为18.97厘米,比报亭陈列的A4尺寸杂志略小,厚为1.34厘米,与一般杂志厚度相似。iPad的重量为0.68千克,约等于5个iPhone的重量。这样的尺寸和重量,使得iPad适合于双手持握或者平放于膝盖上操作,易于旋转方向。但由于它不适合于长时间单手持握,因此,在设计应用时,不应加入甩动之类让用户吃力的操作。同时,iPad适合于放在公文包中携带,而不是像iPhone那样能够放在口袋里(除非口袋足够大)带到野炊场所的。因此,当考虑iPad用户需求时应当想到,很多iPad用户是携带公文包的白领或中产阶级,而非每天在街边嬉戏的年轻学生。
    iPad有着9.7英寸的电容式多点触摸屏,屏幕分辨率为1024×768像素,像素大小为132DPI(Dots Per Inch)。相对于iPhone的480×320像素的屏幕分辨率,iPad屏幕可以显示更多的信息和内容。比如一款含有地图的游戏,在iPhone上,用户需要不停卷动屏幕来浏览地图,而在iPad上,用户的地图翻动操作将会少得多,因此这类游戏更适合于在iPad上开发。iPad容量为16GB,但这将不会成为你的限制。它有着16GHz的苹果A4处理器,能够保证大多数应用流畅运行。但是它的DRAM(Dynamic Random-Access Memory,动态随机存取存储器)容量仅为256MB,因此在开发中需要执行严格的内存管理。iPad下载应用的方式主要为Wi-Fi和3G,而不便于下载大容量的应用,所以应当控制所开发的应用的大小,最好不要超过100MB,否则用户会不乐意去下载你的应用。
    当前,iPad运行的操作系统为iOS 3.2.1。这个版本的操作系统不支持多任务功能,因此你的应用应当能够随时保存状态,防止用户突然退出应用。即便未来iPad所运行的操作系统支持多任务功能了,仍然需要加入这样的考虑,以确保信息安全。
    传感器方面,iPad上有加速度传感器和环境光传感器,其中加速度传感器是这款设备的核心设计之一,它使得在iPad上的任何一款应用都能够支持四个设备方向,也使得一些需要感应设备倾斜的应用成为可能。在设计iPad应用时,还需要考虑到对不同设备方向的支持,这也是苹果官方对所有iPad应用的建议。iPad上可供使用的输入方式有触摸、软件键盘、声音、加速度传感器,输出方式则包括屏幕和声音。这些输入/输出方式应当在应用开发当中多加使用,以丰富用户使用乐趣。

1.2 从PC到iPad Top

    如果要从PC(Mac)平台转向iPad开发,那么需要了解PC开发与iPad开发的几点区别。
    iPad的交互方式以触摸为主,而其他类电脑的交互方式以鼠标和键盘为主。iPad适合用于查看E-mail、上网冲浪、做简单的笔记。完成这些工作不需要大量的键盘输入,人们又希望能够随时随地做这些工作,iPad的携带比任何电脑和笔记本方便很多,所以iPad非常实用。相反地,iPad不适合于文本输入工作,因为人们在软件键盘上的输入速度会比在硬件键盘上慢很多。
    iPad的用户会总是期望无论设备处于何种方向,他们都能够使用各种应用程序。他们会从各种设备方向打开应用,然后将设备在手中反复翻转,期望应用能做出响应。相反地,用户不会去尝试翻转他们的台式机或者笔记本来让应用做出响应。
    还需要考虑到iPad有限的计算和存储能力。iPad上的应用不应当过于消耗计算能力,那样用户会体验到很“卡”的感觉,会给你的应用评“一星”(很低)!在iPad上,应当执行更为严格的内存控制,保证不用的对象得到及时释放,否则应用会因为内存耗尽而崩溃。
    iPad和其他类电脑也有交互。iPad玩家需要用其他类电脑上的iTunes软件来向iPad同步歌曲、电影、书籍,乃至其他更多应用。有一些应用本身也通过Wi-Fi将iPad和其他类电脑连接起来,实现更多的功能。比如iPad应用Brushes,可以在iPad上面用这款应用绘制图形,然后将文件通过Wi-Fi传至其他类电脑上,再通过电脑上的应用程序来重现在iPad上一步一步完成作品的过程!

 

1.3 从iPhone到iPad Top

    很多iPad开发者可能来自iPhone开发阵营。相对于其他类电脑,iPad和iPhone的差异较为微妙,如若不能理解这些微妙的差异,开发出的应用将无法吸引感兴趣的用户。
    先来看看两者的相似之处:它们都有着电容式触摸屏,同样运行着iOS,但在版本和功能上会略有不同;它们都通过同一个App Store来下载应用,iPad甚至可以兼容绝大多数的iPhone应用。那么,为什么不能简单地改动一下iPhone应用使之成为iPad应用呢?为什么还要来专门研究iPad开发呢?
    答案在于那块大了一倍以上的屏幕,它多了三倍以上的像素数量。如果只是将iPhone应用中元素的尺寸简单扩大一倍,那么无异于浪费了iPad的高分辨率,有些元素也会显得很怪异。如果不放大iPhone应用中的元素,而是将它们重新组合在iPad屏幕上,那么你会发现,屏幕上多出了很多空白,空间被浪费了。面对iPad的高分辨率屏幕,每一个iPhone开发者都应当静下心来思考如何重新设计界面元素,重新定义和用户的交互方式,花时间重塑应用,而不要寻找捷径来牺牲用户体验。这也是苹果官方为iPad设计UISplitViewController和UIPopoverViewController的核心思想,我们会在后面的章节中讲到。


1.4 iPad开发的必备知识 Top

    如果你是一名iPhone开发者,那么相信已经掌握了iPad开发的基础知识和工具,可以直接跳至第2章,去学习视图和视图控件的使用。对于初次接触iOS开发的读者,可以从这里开始搜集所需的资料和工具。
    作为一名iPad开发者,首先需要掌握Objective-C语言。Objective-C是一门面向对象的语言,可以看做是C或者C++的超集。Objective-C编译器不仅能编译Objective-C源码,也可以编译C或者C++源码。一个纯Objective-C的源文件的后缀名应当为.m,而一个包含C++代码的源文件的后缀名应当为.mm。本书将在后面的实例中帮助读者逐步熟悉Objective-C的具体语法。
    仅仅掌握Objective-C是不够的,因为如果要想从零开始,一行一行写代码来完成一个应用,将需要很长的时间,这样不仅效率低下,也没有必要。苹果已经为开发者提供了一个很高的起点,只需站在这个起点之上,就能事半功倍。这个起点就是Cocoa—iOS应用程序的框架。事实上,它也是Mac OS X应用程序的框架。Cocoa在iOS操作系统中运行在应用框架(Application Frameworks)层和核心框架(Core Frameworks)层之中。它为开发者提供了基本的应用程序框架、用户界面元素、绘画和图像显示功能、系统交互功能、多线程和内存管理功能、软件国际化功能、文本、网络等。熟悉Cocoa的这些知识,便能调用这些API来快速、高质量地完成你的应用。
    如果是从零开始学Objective-C和Cocoa,本书当中的例子将是最好的参考。书中会对每一个应用的结构做出分析,也会对每一个新出现的语法做出详细的解释。

1.5 iPad开发的必备工具 Top

    要掌握的开发工具主要包括:一个集成开发环境—XCode IDE,一个iPad/iPhone界面创建软件—Interface Builder(书中简称为IB),以及一个性能分析仪器—Instruments。这三件工具也统称为XCode。这些工具可以在苹果官方的iPhone开发中心(iPhone Dev Center)的网站上获取,网址是http://developer.apple.com/iphone/index.action(注意,苹果可能会更新这个网站的网址,因此如果上述网址失效,请在Google上搜索iPhone Dev Center。本书中提到的其他网址也可能会失效,请使用Google搜索找到最新网址)。你需要注册一个Apple ID,用这个ID登录,然后在下载区域下载iOS SDK。iOS SDK里面包含了你所需要的全部工具。
    以上只是做iPad开发所必需的工具。如果想下载最新的iOS SDK,并且希望将所开发出来的应用放在App Store上销售,就需要在上述网站中申请加入iPhone开发者项目(iPhone Developer Program,iDP)。申请获得通过后,将需要支付一定费用来获取为期一年的开发资质(个人为一年99美元,企业为一年299美元)。关于iDP申请和之后的操作,将在本书的最后一章专门叙述。
XCode IDE是苹果公司开发的一个集成开发环境,主要用于iPad、iPhone和Mac开发。它提供了项目模板,你可以直接从模板开始创建iPad应用。它提供了一个方便的界面让你设置编译目标,还提供了一键编译执行功能。它提供了一个模拟器(Simulator),这有两个好处:如果你没有iPad,依然可以在模拟器中来查看你的应用运行情况;你有iPad,通过模拟器来观察代码运行情况会更快捷。但是,模拟器也缺少真机的很多功能,比如它无法模拟重力传感器,不能连接App Store,而且模拟器的运算能力会比iPad强大,因此一些潜在问题可能反映不出来。XCode IDE还提供了良好的调试环境,让你能方便地查找代码中的漏洞。
    Interface Builder能够帮助你快速并且直观地创建用户界面。它有着一个包含了大量界面元素的库,可以直接从库中拖拽界面元素到iPad界面,进行可视化编程。对于初学者来说,IB和XCode IDE的结合会令人头疼,但是一旦跟着本书完成了一两个应用之后,将会很快掌握IB用法。
    Instruments可以帮助了解应用运行过程中的内存使用状况,也可以查出内存的泄露情况。前面强调过,内存管理在iPad开发中至关重要,内存泄露小则导致程序运行效率降低(对于游戏来说,是FPS(Frame per Second,每秒帧率)的降低),大则引起程序崩溃。所以,在代码中需要执行内存管理的思想,在写完代码之后,还要通过Instruments来查漏补缺,确保iPad应用能高效运行。

1.6 第一个iPad应用—Hello World Top

    下面,将开始实现第一个iPad应用—Hello World。这个应用的主界面非常简单,只有一个按钮,上面显示“Hello World Mail”。点击这个按钮后,将弹出一个邮件发送界面,界面上预先写好发送者的邮件地址、邮件主题和邮件正文。通过这个例子,将学习到如何新建iPad应用,如何通过IB来添加界面元素,如何与用户交互,以及如何调用邮件发送界面。


1.6.1   建立工程:HelloWorldMail
    首先,打开XCode,在菜单中选择“File”→“New Project”。选择View-based Application选项,在Product下拉菜单中选中iPad,然后单击Choose按钮,将应用命名为HelloWorldMail,这样就创建好了一个基于视图的应用,如图1-1所示。                                                                                                                                                

  图1-1   新建一个基于视图的应用

    下面来看一看XCode IDE的界面,如图1-2所示。界面上方是工具条,左侧是工程文件浏览区,中间是源码浏览区。
工具条左侧的Overview下拉菜单很重要,我们要在这里选择:
1)真机还是模拟器?在此我们选择模拟器,以方便调试。
2)调试模式还是发布模式?调试模式会包含很多调试符号,发布模式则没有。因此,如果应用在发布模式下崩溃了,那么会得到一些十六进制地址,而非有意义的文字。在调试模式下,编译器不对可执行文件进行优化,但这样也使得调试更为方便。通常情况下,在开发阶段使用调试模式,只有在最后发布时使用发布模式。Dverview的相关设置如图1-3所示。
工具条上最常用的一个按钮是Build and Run,如图1-4所示。单击这个按钮,XCode会对工程进行编译,生成iPad模拟器的可执行文件,并在模拟器上运行HelloWorldMail这个应用。此时,会出现一个空白的屏幕,因为还没有为应用加入任何按钮和功能。                                                                                                             

 图1-2   XCode IDE的界面



 图1-3   Dverview的相关设置


 图1-4   创建与运行按钮

 

1.6.2   工程概览
    在左侧工程文件浏览区,可以看到如图1-5所示的内容,下面一一解释这些文件的用途。 如果是首次接触Objective-C,那么这一小节内容至关重要,在这一小节,我们会介绍很多Objective-C编程的基本概念和方法。


                                                                          

 图1-5   工程文件

 

1. main.m
这个文件中的主函数main(见代码清单1-1)是整个应用执行的起点。其中第一句建立了一个自动释放池;第二句调用UIApplicationMain函数建立起应用对象(application object)和事件循环(event cycle),事实上这个函数永远不会返回,因为它将一直运行,直到用户按下Home按钮为止;第三句释放了前面建立起来的自动释放池,同时也会释放池中的所有变量,这严格执行了内存管理机制。
代码清单1-1   Main函数
int main(int argc, char *argv[]) {
   
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}
作为iPad开发者,不需要修改这个主文件,但是为了帮助你更好地理解程序执行的流程,下面简单介绍程序进入UIApplicationMain函数后如何找到其他文件的入口。
值得注意的是,UIApplicationMain函数的第三个参数指定了该应用从哪个类初始化,它的值为nil,因此程序会默认通过UIApplication来初始化应用对象。UIApplicationMain函数的第四个参数指定了应用的代理从哪个类初始化,它的值为nil,因此程序会默认在HelloWorldMail-Info.plist文件中寻找一个叫做“Main nib file base name”的项,打开这个.plist文件,会发现这个键值为“MainWindow”,因此程序会从MainWindow.xib文件中去寻找应用的代理所属的类。
小知识:对象(object)
Objective-C是面向对象的编程语言,因此对象也是该语言的核心内容。对象中包括了一些相关数据,这些是其成员变量(member variable或者instance variable),除非特别做出声明,否则这些变量只有其所属对象能够操作(读、写、复制等)。对象也提供了一些操作成员变量的方法(method),类似于C++中的函数(function)。
在Objective-C中,永远只需要通过一个对象的指针来操作该对象。空指针是一个特殊的指针,用nil表示(类似于C++中的NULL)。
小知识:消息机制(messaging)
可以给任何一个对象传递一个消息,告诉它去执行某个方法,这个操作叫做消息机制,类似于C++中的函数调用。消息传递的格式为:
[receiver message]
其中receiver为一个对象,而message在源代码中就是一个方法的名称和相应的参数。例如:
[NSAutoreleasePool alloc]
NSAutoreleasePool是一个类,定义了一个自动释放库,在这里就是receiver。alloc是一个方法,在这里就是message。

                                                                                                                                                                                                                                 2. HelloWorldMail-Info.plist
plist指的是属性列表(Property List),包含以XML格式组织起来的数据,每一个数据都有其自己的命名。通常情况下不需要修改plist文件,但有例外,比如想在iPhone上隐藏状态条或指定应用的图标文件。

 

3. HelloWorldMailAppDelegate.h和HelloWorldMailViewController.h
这是两个头文件。以HelloWorldMailAppDelegate.h为例(见代码清单1-2)来简单看一下Objective-C的头文件结构。在学习过程中,可以将Objective-C和C++的语法做出类比,以辅助记忆。
代码清单1-2   HelloWorldMailViewController.h
#import <UIKit/UIKit.h>

@class HelloWorldMailViewController;

@interface HelloWorldMailAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    HelloWorldMailViewController *viewController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet HelloWorldMailViewController *viewController;

@end
第一句#import,是用于包含头文件的语句,类似于C++中的#include。紧随其后,系统文件往往用<>括号括起来,而自己写的文件用""引起来。
第二句@class,是一个指令语句,作用是对一个类做出前向声明,能最小化编译器和连接器所看到的代码的数量。这类似于C++中的前向声明指令class。
从@interface到@end之间,是类的声明。HelloWorldMailAppDelegate是类的名称,而冒号后面的NSObject是其父类名称。<UIApplicationDelegate>声明了一个代理机制:尽管HelloWorldMailAppDelegate没有继承UIApplication类,但是它依然可以调用UIApplication类的一些代理方法。
HelloWorldMailAppDelegate类有两个成员变量,是两个指针,指针所指向的对象分别属于UIWindow类和HelloWorldMailViewController类。UIWindow是一个窗口类,绝大部分的应用都会用到这个类。HelloWorldMailViewController则是在另一个头文件HelloWorldMailViewController.h中定义的一个类。需要注意的是,在Objective-C中,只使用对象的指针,而不是对象本身。这一点可以从这两个成员变量的定义中看出。
再往下面,@property为两个成员变量提供了属性,使得我们可以通过点语法(dot syntax)来对这两个成员变量进行读写操作(access)。关于这一点,在后面还会做出详细说明。

 

4. HelloWorldMailAppDelegate.m和HelloWorldMailViewController.m
这两个文件包含了类的实现。以HelloWorldMailAppDelegate.m为例(见代码清单1-3)来简单说明其中语法和功能。
代码清单1-3   HelloWorldMailAppDelegate.m
#import "HelloWorldMailAppDelegate.h"
#import "HelloWorldMailViewController.h"

@implementation HelloWorldMailAppDelegate

@synthesize window;
@synthesize viewController;

#pragma mark -
#pragma mark Application lifecycle

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions {
   
    // 这个方法通常是这个应用的编程起点
    [window addSubview:viewController.view];
    [window makeKeyAndVisible];

    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
    /*
     当有来电或短消息时,应用进入Inactive状态
     在这个方法中,通常停止计时器、降低OpenGL ES的帧率、暂停游戏
     */
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    /*
     重新启动所有暂停了的任务
     */
}

- (void)applicationWillTerminate:(UIApplication *)application {
    /*
     应用即将彻底结束,所谓彻底结束,是和进入后台相对应的
     */
}

#pragma mark -
#pragma mark Memory management

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
    /*
     应用收到内存警告时,应当尽量多地释放内存,以缓解内存紧张状况
     */
}

- (void)dealloc {
    [viewController release];
    [window release];
    [super dealloc];
}

@end
两个#import用于包含头文件。前面提过了,包含非系统性头文件用""引起来。
在@implementation和@end之间,是对HelloWorldMailAppDelegate类的实现。其中@synthesize和头文件中的@property成对出现。#pragma mark只是用于分隔源代码的,不对代码运行产生影响。下面来看一看每一个方法(类似于C++中的函数)的作用。
小知识:Objective-C中的方法(Method)
以HelloWorldMailAppDelegate.m的第一个方法为例。
-(BOOL)application:(UIApplicaiton*)application
   didFinishLaunchingWithOptions: (NSDictionary*)launchOptions
其中application和didFinishLaunchingWithOptions是这个方法的签名(signature),application和launchOptions是这个方法的参数,这两个参数的类型分别为UIApplication*和NSDicitonary*。
可以看出,Objective-C方法的签名和参数可以交互出现,一个签名后面跟随一个参数(在C++中,函数的参数都在括号之中,和函数名是分开的)。Objective-C中这样做的好处是,每个参数的类型和意义非常明确,代码可读性很强。
方法开头的(BOOL)表示这个方法的返回值是BOOL类型的。方法开头的减号(-)表明这是一个实例方法(instance method),意思是这个方法的调用者必须是一个类的实例(instance),而不能是一个类(class)本身。相反地,方法开头如果有加号(+),则表明这是一个类方法(class method),意思是这个方法的调用者必须是一个类。
第一个方法的签名为application: didFinishLaunchingWithOptions:,在应用启动后自动被调用。第二个方法的签名为applicationWillResignActive:,在应用即将从活动状态转入非活动状态时被调用。第三个方法的签名为applicationDidBecomeActive:,在应用刚刚转入活动状态后被调用。第四个方法的签名为applicationWillTerminate:,在应用即将退出时被调用。第五个方法的签名为applicationDidReceiveMemoryWarning:,在应用得到内存警告后被调用。以上5个方法都是代理方法(delegate method)。需要注意的是第一个方法中的
[window addSubview:viewController.view];
这一句的作用就是将viewController的视图作为子视图加在window上面。程序启动后,用户所看到的也是viewController.view。因此,如果要修改呈献给用户的内容,就需要修改viewController所属的类HelloWorldMailViewController。
小知识:代理(Delegation)与协议(Protocol)
代理是iPhone开发中一个很重要的设计模式。简单地说,在代理模式中,一个对象(在例子当中就是UIApplication)给其代理(HelloWorldMailAppDelegate的实例)发送消息,向它要求输入或者通知它一个事件即将或者刚刚发生。代理模式的好处是,即使HelloWorldMailAppDelegate不继承UIApplication,它依然可以使用UIApplication的一些方法。在C++中,继承(Inheritance)是重复利用代码的主要方法;而代理则提供了另一种方法,很多时候会比继承更方便。
由于人们希望一些方法能够经常为代理所使用,于是将这些方法合在一块,组成协议。HelloWorldMailAppDelegate采用(Adopt)了UIApplication协议,也就是说它能够执行UIApplication的一些代理方法。
最后一个方法的签名是dealloc。这个方法继承自NSObject类。它非常重要,只有正确地实现它才能够确保无内存泄露。在dealloc中,需要释放所有为HelloWorldMailAppDelegate所拥有的对象。

 

5. HelloWorldMailViewController.xib和MainWindow.xib
xib文件中包含了视图的信息。如果双击一个xib文件,就会启动IB,可以进行“所见即所得”的操作。

 

6. HelloWorldMail_Prefix.pch
这是一个包含文件,会和其他文件分开编译,因此不需要在其他文件之中包含它。

 

7. UIKit.framework, Foundation.framework和CoreGraphics.framework
这是三个最常用的框架(framework),类似于C++中的库(library)。

 

8. HelloWorldMail.app
这就是在iPhone上的可执行文件了!

1.6.3   添加按钮
这一小节中,我们要让用户能够在应用启动后,看到一个按钮,上面写着“Say Hello World through Mail!”。上一节中提到了,我们需要修改HelloWorldMailViewController这个类,包括其xib文件、h文件和mm文件。为了达成这一目标,需要修改界面文件、修改源文件以及给两者建立起连接。
我们先来修改xib文件。双击HelloWorldMailViewController.xib文件,能看到IB启动,并且呈现出一个大大的白色视图—这就是用户在程序启动后将看到的视图。下面请选择Interface Builder→Tools→Library,将看到一个包含大量界面元素的库(如图1-6所示)—将从这里开始熟悉可用于装饰界面并呈现信息的元素。请找到一个叫做Round Rect Button的按钮元素,将其拖到视图中央。双击这个按钮,可以输入这个按钮上将要显示的文字;本程序中输入的是“Say Hello World through Mail!”(如图1-7所示)。

                                                                
                                                                                        图1-6   界面元素库Library

                                                                         
                                                                                                         图1-7   含有按钮的界面

 

    此时,可以单击工具条中的Build and Run按钮,查看含有按钮的iPad界面,如图1-8所示。这时的按钮还不具备任何功能,因此下面我们所要做的是为按钮添加一点功能—当单击时在调试区域显示“Hello World!”。我们通过修改源文件来添加一个在调试区域显示字符串的方法。请打开HelloWorldMailViewController.h,在@end前面加入以下方法声明:
-(IBAction) didClickButton;
然后打开HelloWorldMailViewController.m,为我们刚刚声明的方法添加实现:
-(IBAction) didClickButton {
    NSLog(@"Hello World!");
}
NSLog是做开发调试的最好伙伴,可以用它在程序运行时显示想查看的参数内容,也可以用它来检测代码流程。上面这句的参数@"Hello World!"是要显示的字符串。
至此,我们在xib文件中添加了按钮,也在源文件中添加了按钮按下后要实现的方法,但两者之间的连接尚未建立—这个很简单,请打开HelloWorldMailViewController.xib文件,按住Control键不要松开,同时将鼠标从视图上的按钮拖向File誷 Owner。这时可以看到一个弹出框,如图1-8所示。单击didClickButton,这样连接就建立好了。
                                                                                         

                                                                                                       图1-8                                                                                                    

 
此时,请再次单击Build and Run按钮,运行iPad模拟器。按下界面上的按钮,看看调试区域是否显示出了“Hello World!”,如果显示出来了,那么按钮已经具备了基本功能。但只和自己说Hello World是不够的,我们将在下一小节讲解怎么使用邮件编辑器,向全世界任何一个在使用互联网的人说Hello World!

1.6.4   邮件编辑器
使用邮件编辑器,需要完成以下步骤:添加框架(framework),包含头文件,设置代理,完成代理方法。本小节将讲解如何一步一步完成这些工作。当今后学习如何播放音乐、视频,如何调用系统图片库时,将需要完成同样的步骤。
首先,你需要向工程中添加框架MessageUI.framework。在工程文件浏览区域,找到Frameworks组,右键单击后,选择Add→Existing Frameworks,然后在弹出框中选中MessageUI.framework。
接下来,打开HelloWorldMailViewController.h文件,用下面这句来包含所必需的头文件:
#import <MessageUI/MessageUI.h>
然后是将HelloWorldMailViewController设置为MFMailComposeViewController的代理。需要添加<MFMailComposeViewControllerDelegate>,使得文件成为:
#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>

@interface HelloWorldMailViewController : UIViewController <MFMailComposeViewControllerDelegate> {

}

-(IBAction) didClickButton;

@end
最后打开HelloWorldMailViewController.m,如代码清单1-4所示的修改didClickButton方法,并添加签名为mailComposeController: didFinishWithResult: error:的代理方法。在didClickButton中,我们首先通过[MFMailComposeViewController canSendMail]判断设备的邮件发送功能是否已经设置好。如果设置好了,我们就初始化一个MFMailComposeViewController的实例picker,并且将其代理设置为self—也就是这个消息的接收对象。接下来,我们用setSubject:和setMessageBody: isHTML:两个方法来预设邮件编辑器的主题和正文。此时,邮件编辑器已经设置好了,我们用presentModalViewController: animated:方法来显示邮件接收器。最后,由于我们使用了alloc来创建picker,并且它已经呈现在屏幕上了,因此我们需要release掉。再看看这个代理方法,它在用户发送完邮件或者点击cancel(取消)按钮后被触发。这时,我们需要移除掉picker,方法是dismissModalViewControllerAnimated:。
代码清单1-4   HelloWorldMailViewController.m中两个需要修改的方法
-(IBAction) didClickButton {
    if ([MFMailComposeViewController canSendMail]) {
        MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
        picker.mailComposeDelegate = self;

        [picker setSubject:@"Hello World from my iPad app!"];

        NSString *emailBody = @"Hey, <br /> <br /> \
        this is from my first iPad app!";
        [picker setMessageBody:emailBody isHTML:YES];

        [self presentModalViewController:picker animated:YES];
        [picker release];
    }
}

- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
    [self dismissModalViewControllerAnimated:YES];
}
此时,再运行iPad应用,你可以打开邮件编辑器了(如图1-9所示)。你可以试着用这款应用给朋友发送邮件,看看他们能不能收到你的问候呢?

                                                                                              
                                                                                              图1-9   邮件编辑器

1.7 小结 Top

    在这一章中,我们简要地介绍了iPad性能,并与电脑和iPhone做了比较,以强调其主要特性:高分辨率的屏幕、触摸式交互以及不错的移动性能。通过这些比较,我们为iPad应用的设计提供了建议。接下来,我们谈到了iPad开发所必需的知识(Objective-C和Cocoa)和工具(XCode IDE)。最后,我们通过一个邮件编辑器实例,来帮助完成你的第一个iPad应用。在例子当中,我们介绍了最基本的Objective-C语法以及XCode IDE使用方法。有了这一章的基础,你可以开始学习更多的iPad开发技术了!

第2章 视图和视图控制器 Top

2.1   视图(UIView)
2.2   视图控制器(UIViewController)
2.3   弹出控制器(UIPopoverController)
2.4   分割视图控制器(UISplitViewController)
2.5   实例:iPad网页导航
2.6   小结
    从这一章开始,将学习iPad开发中各个精彩纷呈的技术,包括视图、动画、影音播放、手势等。每一章为一个专题,其中前几个小节用于介绍概念和API,最后会讲述一个例子。
    这一章介绍视图和视图控件。视图(UIView)是应用和用户的视觉交互的主要场所,它也承担了接收手势并且做出响应的任务。我们会先讨论它的概念以及常用API,然后介绍其重要子类,包括窗口(UIWindow)和网页视图(UIWebView)。视图控制器(UIViewController)则提供了基本的视图管理,在讨论其概念和用法之后,会介绍其常用子类,包括导航控制器(UINavigationController)和标签控制器(UITabBarController)。之后,会分别介绍分割视图控制器(UISplitViewController)和弹出控制器(UIPopoverController),这是两个iPad专用的视图控制器,体现了充分利用iPad的高分辨率触摸屏的思想。最后,将呈现一个网页导航的例子,帮助理解和运用本章的知识。

2.1 视图(UIView) Top

2.1.1   视图概览
作为应用和用户进行视觉和触摸交互的主要对象,视图值得花时间来了解。应用在后台进行的大量计算的结果,主要通过视图来输出给用户,而用户的绝大部分输入都通过触摸视图来完成。
最底层的视图对象是包含在窗口(UIWindow)对象之中的;每一个视图对象之上,又有着其子视图对象。因此呈现在iPad用户面前的,往往是一系列叠放在一块的视图对象。视图有两个属性,反映了它的等级关系:superview(父视图)和subviews(子视图)。一个视图对象往往有着多个子视图对象,因此subviews的类型是NSArray*。一个视图对象如果是底层视图对象,那么它的superview就是nil(空指针)。
视图有几个关键属性,用于限定其位置和大小。center表示其相对于superview的中心位置;frame是一个相对于superview的矩形,表示视图对象所在区域;bounds是一个相对于self的矩形,表示视图对象的绘图区域,这个矩形通常和frame同样大小,但这并不是必须的。请注意视图的坐标系统,原点在左上角,X轴方向为从左到右,而Y轴方向为从上到下,如图2-1所示。
                                                                                
 
                                                                                     图2-1   视图坐标系统示意图
视图的transform(变换)属性规定了视图的显示方式。如果想旋转、缩放、移动视图,那么只需修改视图的变换矩阵(transform matrix)。视图的transform的改变可以用动画显示出来,调用beginAnimations:context:和commitAnimations方法即可动画显示视图transform的改变。类似的,也可以用动画来显示视图的frame、bounds和center的改变。
视图的子类构成了iPhone和iPad的主要屏幕元素。它们包括:网页视图(UIWebView)、搜索条(UISearchBar)、滚动视图(UIScrollView)、警告视图(UIAlertView)、操作单(UIActionSheet)、工具条(UIToolBar)、图像视图(UIImageView)、标签(UILabel)和窗口(UIWindow)等。下面介绍一些重要的子类。

 

2.1.2   窗口(UIWindow)
窗口是视图的一个子类。窗口的主要功能:一是提供一个区域来显示视图,二是将事件(event)分发给视图(请参考本节的小知识来了解iOS中的事件分发机制)。一个应用通常只有一个窗口,但也有例外,比如在一个iPhone应用中加载一个电影播放器,这个应用本身有一个窗口,而电影播放器还有另一个窗口。
小知识:iOS中的事件类型和分发
iOS设备上有很多硬件能够因用户的行为而产生数据,包括触摸屏、加速度传感器和陀螺仪。当原始数据产生后,系统的一些框架会对这些原始数据进行封装,并作为事件传递给正在运行的应用来进行处理。
当应用收到一个事件后,会先将其放在事件队列(event queue)当中。应用的singleton从事件队列中取出一个事件并分发给关键窗口(key window)来处理。
如果这个事件是一个触摸事件的话,那么窗口会将事件按照视图层次传递到最上层(用户可见)的视图对象,这个传递顺序叫做响应链(responder chain)向下顺序。响应链最下层(也是视图最上层)的视图对象如果不能处理这个事件,那么响应链的上一级的视图将得到这个事件并尝试处理这个事件,如果不能处理的话就继续向上传递,直到找到能处理该事件的对象为止。
如果这个事件不是一个触摸事件,那么事件的分发机制有所不同,在此不做介绍。

 

2.1.3   网页视图(UIWebView)
网页视图是一个可以帮助你在应用中嵌入网页内容的类。只需要完成简单的三个步骤来显示网页内容:创建一个网页视图对象,将它放在一个窗口当中,然后用loadRequest:方法载入网页内容。
可以通过stopLoading类停止网页载入,也可以通过一些代理方法在网页载入过程中添加自己的代码。这些代理方法有webView:shouldStartLoadWithRequest:navigationType、webViewDidStartLoad:、webViewDidFinishLoad:以及webView:didFailLoadWithError:。顾名思义,这4个代理方法的触发分别在开始载入网页内容之前、开始载入之后、成功载入之后以及载入失败之后。需要注意的是,如果设置了网页视图对象的代理,那么在释放这个网页视图对象前,需要将代理设置为nil。
还可以使用goBack和goForward方法来进行网页导航。另外,网页视图除了支持HTML内容的显示之外,还能用于显示Excel文件、Keynote文件、Numbers文件、Pages文件、PDF文件、PowerPoint文件以及Word文件等。
在2.5节的例子中,我们将使用网页视图来显示各个著名网站的内容。

 

2.2 视图控制器(UIViewController) Top

2.2.1   视图控制器概览
视图控制器提供了基本的视图管理功能,包括显示模态视图(modal view)和根据设备方向(orientation)调整视图。通常使用视图控制器来管理全屏视图以及它的子视图,而不会用来管理一个只占有一小块屏幕的视图。
注意,视图控制器也是前面提到的响应链中的一环。当一个视图获得一个触摸事件并且无法做出响应时,它的视图控制器将获得这个事件,并做出响应或者继续向上传递这个事件。
通常来说,一个视图控制器管理一个视图及其子视图。但是当视图之间的关系变得复杂起来时,需要用导航控制器和标签视图控制器来管理多个视图控制器。模态视图用于将一个视图控制器的内容弹出到整个iPhone屏幕上,而在iPad上,不使用模态视图,使用弹出控制器,因为iPad屏幕较大,不能用整个屏幕来显示一小块信息。另外在iPad上,分割视图控制器也是一个标准控件,可以充分利用屏幕空间。
在程序中使用视图控制器来管理全屏视图是一个良好的习惯。一方面可以将视图和数据分隔开来,另一方面可以充分利用Cocoa中现有代码,节省设计的工作量。

 

2.2.2   导航控制器(UINavigationController)
如果应用中的视图有着树状关系—也就是说,从一个主视图出发,用户可以选择多个视图,并且每个视图又有着自己的分支—那么导航控制器是首先应当考虑的控制器,如图2-2所示。与前一小节所介绍的视图控制器不同的是,不能继承导航控制器,而应直接使用。

                                   
                                                                                        图2-2   导航控制器(摘自苹果官网)
图2-2显示了导航控制器的流程。最左侧是根视图,当用户点击其中的General项时,General视图会滑入屏幕;当用户继续点击Auto-Lock项时,Auto-Lock视图将滑入屏幕。相应地,在对象管理上,导航控制器使用了导航堆栈。根视图控制器在堆栈最底层,接下来入栈的是General视图控制器和Auto-Lock视图控制器。可以调用pushViewControllerAnimated:方法将视图控制器推入栈顶,也可以调用popViewControllerAnimated:方法将视图控制器弹出堆栈。
视图控制器有两个代理方法较为常用。一个是navigationController: willShow ViewController:animated:,它在一个视图控制器入栈之前被触发;另一个是navigation Controller:didShowViewController:animated:,它在一个视图控制器入栈之后被触发。
任何一个视图控制器都有几个与导航控制器相关的属性:navigationController、navigationItem、editing、hidesBottomBarWhenPushed以及toolbarItems等。如果一个视图控制器在导航堆栈中,navigationController返回的是相应的导航控制器;如果它不在任何导航堆栈中,则返回nil。

 

 

2.2.3   标签控制器(UITabBarController)
上一小节说过,导航控制器适合于管理树状关系的视图。而当少数几个视图具有并列关系时,就需要考虑使用标签控制器。与导航控制器一样,应当直接使用标签控制器,而不是去继承它。
如图2-3所示,时钟程序具有4个功能(时钟、闹钟、秒表以及定时器),以4个视图呈现,这4个视图之间是并列关系,并且各由一个视图控制器管理。标签控制器的每个标签都对应着一个视图控制器。用户可以通过底部的标签条来选择标签。

          
                                                                                              图2-3   标签控制器(摘自苹果官网)
标签控制器有一个viewControllers属性,包含了每一个标签所对应的视图控制器。如果向viewControllers中添加了6个或6个以上的视图控制器,在标签条中只会显示4个标签以及一个More选项。当用户单击More时,剩下的标签会显示出来。
标签控制器有一个常用的代理方法:tabBarController:didSelectViewController:。当用户选择一个标签之后,这个方法被触发,用户可以添加自己定义的功能。

2.3 弹出控制器(UIPopoverController) Top

    前面所介绍的导航控制器和标签控制器都是iPhone中的全屏控制器,它们的视图占满整个iPhone屏幕,用于呈现导航信息。但是如果在iPad应用中继续使用它们作为全屏控制器,那么大量的屏幕空间将被浪费。因为iPhone的屏幕分辨率为320×480,而iPad的屏幕分辨率是768×1024。那么怎样在iPad中使用一部分空间来呈现导航信息呢?答案是使用弹出控制器。
    弹出控制器暂时性地使用部分屏幕空间来呈现信息。它的视图会显示在其他视图上方,但是只占用一小块屏幕区域。如图2-4所示,左下角为Saved Photos弹出视图。当用户单击该区域以外任何地方时,它的视图会消失(passthroughViews是一个例外,允许指定一些视图,与它们的交互不会让弹出控制器的视图消失)。

                                                               
                                                                                       图2-4   左下角为弹出控制器的视图
弹出控制器的内容由另外的视图控制器提供,在初始化弹出控制器时,需要为它提供另一个视图控制器,方法是initWithContentViewController。弹出控制器的呈现有两种方法,分别是presentPopoverFromRect:inView:permittedArrowDirections:animated和presentPopover FromBarButtonItem:permittedArrowDirections:animated。前者由一个矩形区域弹出,后者由一个按钮弹出。让弹出控制器消失可以使用方法dismissPopoverAnimated:。
    如果想让弹出控制器随着设备方向旋转,最好的方法是让UIViewController来完成。具体实现方法是,弹出控制器会先将弹出视图隐藏起来,然后在旋转之后重新展现。在本章后面的例子中将解释这是如何完成的。
    弹出控制器有两个代理方法。popoverControllerShouldDismissPopover:在用户取消弹出视图时被触发;popoverControllerDidDismissPopover:在弹出视图因用户行为被取消后被触发(注意,如果不是因用户行为被触发,而是因为调用了dismissPopoverAnimated:方法,那么这个代理方法不会被触发)。
    在本章最后的例子当中,将使用弹出控制器来显示网站的导航信息。

 

2.4 分割视图控制器(UISplitViewController) Top

    分割视图控制器是iPad专用的全屏控制器(可以尝试在iPhone上使用它,可能会导致程序崩溃)。它使用一小部分屏幕来显示导航信息,然后使用剩下的大部分屏幕来显示相关的详细信息。导航信息由一个视图控制器来管理,详细信息由另一个视图控制器来管理。在创建分割视图控制器后,应当给它的viewControllers属性添加这两个(不能多也不能少)视图控制器。分割视图控制器本身只负责协调二者的关系以及处理设备旋转事件(如弹出控制器一样,最好不要亲自来处理设备旋转事件)。
    分割视图控制器有三个代理方法。
splitViewController:willHideViewController:withBarButtonItem:forPopoverController:用于通知代理一个视图控制器即将被隐藏。这通常发生在设备由landscape旋转到portrait方向时。
splitViewController:willShowViewController:invalidatingBarButtonItem:用于通知代理一个视图控制器即将被呈现。这通常发生在设备由portrait旋转到landscape方向时。
splitViewController:popoverController:willPresentViewController:用于通知代理一个弹出控制器即将被呈现。这发生在portrait模式下,用户单击屏幕上方的按钮弹出导航信息时。
    下面一节的例子就是基于分割视图控制器的,可以详细了解它的使用。

2.5 实例:iPad网页导航 Top

2.5.1   基于分割视图控制器的应用
先建立一个新的工程,模板为Split View-based Application,命名为Websites。直接运行程序,可以在模拟器中看到分割视图在portrait模式和landscape模式下的样子(在iPhone Simulator的Hardware菜单中有Rotate Right和Rotate Left项,以便于选择模拟设备的方向)。如图2-5所示,在portrait模式下,屏幕的绝大部分被详细视图(Detail View)所占据,要想查看导航信息,需要点击屏幕左上方的按钮,来显示一个弹出视图(Popup View)。而在图2-6所示的landscape模式下,详细视图占据约60%的屏幕面积,剩下的部分用一个表格视图(Table View)专门显示导航信息。不论是导航信息还是详细视图中的信息,两种模式下的信息内容都一致,只是显示的方式不同。

                                                                  
                                                                                           图2-5   portrait模式的分割视图

                                                      
                                                                                           图2-6   landscape模式的分割视图
为了更好地理解应用的工作原理,打开MainWindow.xib文件,在弹出的图标中双击打开Split View Controller。这时可以看到,分割视图中包含两个视图,左侧是表格视图,由RootViewController管理,右侧是详细视图,由DetailViewController管理。
下面我们要实现的网页导航应用中,表格视图中能显示两组网站,一组是搜索引擎(包括Google和Bing),另一组是新闻(包括FoxNews和NewYorkTimes)。当用户单击表格视图中的任意一项时,在右侧的网页视图中会打开相应的网页。

 

2.5.2   网页视图
下面我们来实现网页视图的功能。
网页视图应当放置在详细视图中。打开DetailView.xib文件,从Library中拖出一个Web View到DetailView视图中,并调整为全屏。在DetailViewController.h中添加以下代码:
UIWebView* webView;
@property (nonatomic, retain) IBOutlet UIWebView* webView;
其中第一句添加在花括号以内,第二句添加到花括号外面。在DetailViewController.m中添加以下代码:
@synthesize webView;
[webView release];
其中第一句添加到@implementation DetailViewController后面,第二句添加到dealloc方法中。最后再次打开DetailView.xib文件,按住Control键的同时,从File誷 Owner拖向网页视图,然后在弹出框中单击webView。这样,我们在视图中添加了网页视图,并从代码中用webView指向这个网页视图。这也是基于xib文件更改视图的基本操作方式,以后不再赘述。
我们希望网页视图能尽量充满详细视图,并且能让用户拖动,因此需要在IB里做一点改动。打开DetailView.xib,点击网页视图,打开Size Inspector,修改其Autosizing属性,如图2-7所示。如果不这样改动,你的代码最终虽然能够运行,但是网页视图的一部分是看不到的。

                                                                                   
                                                                              图2-7   修改网页视图的Autosizing属性

下面添加代码,使得用户选择一个网站时,能够更新详细视图中的webView。具体在哪里添加这些代码呢?详细视图得到更新的流程如下:
1)用户从表格视图中选择一项。
2)RootViewController的tableView:didSelectRowAtIndexPath:被触发(这是一个代理方法)。
3)DetailViewController的setDetailItem:被调用(你可能会发现你找不到这个方法的定义—这个方法在detailItem的属性中得到设置,见随后的小知识)。
4)DetailViewController的configureView方法被调用,详细视图得到更新。
在configureView方法当中,加入以下代码:
// 网页视图加载请求
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:detailItem]]];
代码中,loadRequest:方法的参数是NSURLRequest*类型的一个URL请求。这个请求的初始化依赖于detailItem,而detailItem由用户的选择决定。
小知识:retain属性的默认实现
为一个对象设置属性,相当于为其定义了getter和(或)setter。我们来看一下retain属性的默认实现。若代码中有以下属性:
@property (retain) Class* obj;
@synthesize obj;
相当于定义了以下的getter和setter:
-(Class*) getObj {
    return obj;
}
-(void) setObj:(Class*)anotherObj {
    if (obj!=anotherObj) {
        [obj release];
        obj = [anotherObj retain];
    }
}
注意,在setter当中,我们首先对obj执行release操作,这是因为:如果obj原先已经指向一个对象的话,那个对象已不再需要,就应该释放掉以避免内存泄露;如果obj原先没有指向任何对象,那么它的值为nil,给nil发送消息相当于什么也不做,不会有任何问题。

2.5.3   表格视图
在表格视图中,首先设置有多少个section,也就是用多少组网站。打开RootViewController.m文件,修改numberOfSectionsInTableView:的返回值为2,这样表格视图中就有了两个section,我们就有了两组网站。
- (NSInteger)numberOfSectionsInTableView:(UITableView *)aTableView {
    //小节(section)的数目
    return 2;
}
接下来为每一个section也就是每一组网站添加标题。添加以下方法:对第一个section,返回@"Search",这样第一组网站的标题就是Search。同理,第二组网站的标题就是News。
- (NSString*)tableView:(UITableView*)aTableView titleForHeaderInSection:(NSInteger)section {
     if (section==0) {
          return @"Search";
     }
     else {
          return @"News";
     }
}
再接下来,设置每一个section中有多少行,也就是每一个组中有多少个网页。修改tableView:numberOfRowsInSection:方法如下:
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {
    // 每一个小节中的行数
    return 2;
}
这样每一组中都有两个网页。
类似于前面设置section标题,我们设置每一行显示的标题,也就是每一个网站的名称。修改tableView:cellForRowAtIndexPath:方法如下:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
   
    static NSString *CellIdentifier = @"CellIdentifier";
   
    // 重复使用一些单元(cell)
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
        cell.accessoryType = UITableViewCellAccessoryNone;
    }

     // 找出小节和行数
     int section = [indexPath indexAtPosition:0];
     int row = [indexPath indexAtPosition:1];

     // 单元标题
     NSString* cellTitle;
     if (section==0) {
          if (row==0) {
               cellTitle = @"Google";
          }
          else {
               cellTitle = @"Bing";
          }

     }
     else {
          if (row==0) {
               cellTitle = @"Fox News";
          }
          else {
               cellTitle = @"NY Times";
          }
     }
   
    // 设置这个单元
    cell.textLabel.text = [NSString stringWithFormat:cellTitle, indexPath.row];
    return cell;
}
其中cell是每一行的内容(可以包含图片)。找到对应于indexPath的section和row,然后给出相应的标题cellTitle。最后将每一行的文字设置为cellTitle。
到此为止,我们已经能够显示出完整的导航信息了,但是这些信息还没有与webView联系起来。我们所需要做的是修改tableView:didSelectRowAtIndexPath:方法。定义一个stringURL用于储存所要打开的网址;从indexPath中计算出section和row;根据section和row给stringURL赋值;最后调用DetailViewController的setDetailItem方法,打开相应网页。具体代码如下:
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // 网站URL
     NSString* stringURL;

     // 小节和行数
     int section = [indexPath indexAtPosition:0];
     int row = [indexPath indexAtPosition:1];

     // URL
     if (section==0) {
          if (row==0) {
               stringURL = @"http://www.google.com";
          }
          else {
               stringURL = @"http://www.bing.com";
          }

     }
     else {
          if (row==0) {
               stringURL = @"http://www.foxnews.com";
          }
          else {
               stringURL = @"http://www.nytimes.com";
          }
     }

    detailViewController.detailItem = stringURL;
}
至此,一个简单的网页导航应用已经完成,可以运行看看结果,如图2-8所示。

                                      
                                                                                                     图2-8   网页导航实例

 

2.6 小结 Top

    在本章中,介绍了视图的基本功能:呈现视觉信息,接收触摸事件。然后介绍了两类重要的视图:窗口和网页视图。
视图控制器是MVC(Modal-View-Controller,模型、视图、控制器)设计模式的核心部件,也是在绝大多数iOS应用中负责视图逻辑的部件。导航控制器和标签控制器是iOS应用中的两类重要控制器,而分割视图控制器和弹出控制器则是iPad所专有的两种控制器。
在最后以一个网页导航应用为例,介绍了视图及其控制器的使用方法。在后面的章节中,还会从触摸事件的传递、内存管理等多个方面加强讨论视图的概念和使用。

第3章 内 存 管 理 Top

3.1   对象所有权、引用计数与释放
3.2   自动释放与便捷方法
3.3   访问器方法与属性
3.4   改变引用计数的特殊情况
3.5   内存管理总结
    在前面几章中,通过学习代码实例和一些小知识点,零零碎碎地了解了内存管理的一些基本原则。但是内存管理对于iPad开发非常重要(对于iPhone、MacOS开发也同样重要),如果掌握不好,轻则程序运行效率会受到影响,重则会导致程序崩溃,这是用户无法忍受的,开发者也不应容忍这样的事情发生。因此在对iPad开发进行更深入学习之前,有必要用一章的篇幅来详细地叙述内存管理的概念和方法。这样在后面的学习中,就能够更自如地掌控所创建的对象。
    这一章将阐述对象所有权的概念,如何放弃所有权以及对象的生命周期;然后解释一类特殊的对象释放方法—自动释放,特殊的对象创建方法—便捷方法;接下来讨论与访问器相关的内存管理原则,对象属性的默认实现;之后介绍Nib对象的内存管理规则;最后对所阐述的概念和规则做一个总结,以便于记忆内存管理方法。

3.1 对象所有权、引用计数与释放 Top

    当一个所有者(owner,其本身可以是任何一个Objective-C对象)做了以下某个动作时,它就拥有了对一个对象的所有权(ownership):
1)创建一个对象。包括使用任何名称中包含“alloc”、“new”或者“copy”的方法。
2)保留(retain)一个对象。
一个对象可以有多个所有者,一个所有者也可以拥有多个对象。
相应地,引用计数(reference count)增减的基本规则是:
1)当所有者创建一个对象时,该对象的引用计数为1。
2)当所有者保留它时,该对象的引用计数加1。
3)当所有者释放(release)它时,该对象的引用计数减1。
与此相关,当一个所有者对于一个对象的引用计数的增减总计为0时,它就放弃了对这个对象的所有权。
    现在可以从两个不同的角度来看Objective-C的内存管理问题。从对象所有权的角度来看,当一个对象有着至少一个所有者的时候,它依然存在;当它没有任何所有者的时候,它会被释放掉。从引用计数的角度看,一个对象存在时,其引用计数大于零;当一个对象的引用计数为零时,它会调用dealloc方法并被释放掉。这两个角度的关系是:在所有权的背后起作用的机制是引用计数机制,通过引用计数的增减来理解所有权的概念。只能使用所有权的概念来管理内存,如果试图直接获取对象的引用计数,那么得到的数将会令人意外,因为系统的一些框架会“偷偷”增减对象的引用计数。
    回顾一下,内存管理的目标是:当一个对象的某个所有者依然需要使用它时,要保证这个对象的存在;当一个对象的所有所有者都不再需要它时,要保证这个对象被销毁。因此只要任何一个所有者在使用完一个对象之后释放掉它,那么以上内存管理的目标就可以实现。由此可以得出任何一个所有者(记住,所有者本身也只是一个对象)所应当遵守的基本步骤:
拥有一个对象→使用一个对象→放弃对象的所有权(释放对象)
    从引用计数的角度来看就是:
还需要这个对象时,保持对其增减为正;不再需要这个对象时,不增减其引用计数值。
如图3-1所示,所有者1和所有者2单独地执行了拥有对象、使用对象、放弃对象所有权的步骤;当所有者1不再需要该对象时及时放弃了所有权,但此时所有者2依然拥有该对象,因此该对象依然存在,所有者2可以继续使用它;当所有者2也不再需要该对象时,也放弃所有权,这时对象不再有任何所有者(相应的引用计数变为0),因此立刻被销毁掉。

                                           
图3-1   两个所有者对同一个对象的内存管理
需要注意的是,所有者2只是复制了该对象的指针,并没有使用copy方法,因此复制指针这个操作本身并不增加对象的引用计数。而正因为所有者2希望能使用该对象,因此通过retain方法成为它的所有者,也保证了在所有者1放弃该对象时,该对象不被销毁。

 

3.2 自动释放与便捷方法 Top

    有时候,一个所有者创建一个对象后,会立刻将该对象的指针传递给其他所有者。这时,这个创建者不希望再拥有这个对象,但如果立刻给它发送一个release(释放)消息,则这个对象被立刻释放掉,这样会导致其他所有者还没有来得及保留该对象。解决这个两难问题的方法是,给对象发送一个autorelease(自动释放)消息,这样创建者不再拥有该对象的所有权;该对象成为自动释放的对象,但是不会立刻被释放掉,其他所有者可以有时间保留或复制该对象,并成为其唯一所有者。
来看一个自动释放的例子。一个所有者先用alloc方法创建一个对象,此时该所有者拥有这个对象,对象的引用计数为1。紧接着,所有者自动释放该对象,即放弃了所有权,但对象的引用计数在一段时间内依然为1。我们可以看出自动释放的另一个好处,就是不会因为在后面忘记给对象发送release消息而造成内存泄露。相关代码如下所示:
-(Object*)returnAutoreleaseObject {
    Object* obj = [[Object alloc] init];
    return [obj autorelease];
}
与自动释放相关的有一大类构造方法,由它们构造的对象直接就是自动释放的对象,这一类构造方法叫做便捷方法。比如下面这个字符串就是一个自动释放的对象,stringWithFormat:就是一个便捷方法。
NSString* string = [NSString stringWithFormat:@"autoreleaseString"];
再举几个便捷方法的例子,方便读者以后的开发。
   NSArray的arrayWithObjects:和arrayWithArray。
   UIImage的imageNamed。
   NSNumber的numberWithBool。
如上所述,autorelease方法会在一段时间以后释放掉一个对象,在这段时间内可以安全地使用该对象。那么这段时间究竟是多久呢?我们需要先更多地了解自动释放的机制,再来回答这个问题。
先来看看自动释放池。自动释放池是NSAutoreleasePool的实例,其中包含了收到autorelease消息的对象。当一个自动释放池自身被销毁(dealloc)时,它会给池中每一个对象发送一个release消息(如果给一个对象多次发送autorelease消息,那么当自动释放池销毁时,这个对象也会收到同样数目的release消息)。可以看出,一个自动释放的对象,它至少能够存活到自动释放池销毁的时候。
那么自动释放池何时被创建,又何时被销毁呢?在每一个事件周期的开始,系统会自动创建一个自动释放池,在每一个事件周期的结尾,系统会自动销毁这个自动释放池。一般情况下,可以理解为:当用户的代码在持续运行时,自动释放池是不会被销毁的,这段时间内用户可以安全地使用自动释放的对象。当用户的代码运行告一段落,开始等待用户输入(或者其他事件)时,自动释放池就会被释放掉,池中的对象都会收到一个release消息,有的可能会因此被销毁。
到此为止,已经对自动释放的机制有了一个大体的了解。自动释放而非直接释放,可以节省一些代码量,提高开发速度。但是它有一个明显的缺点:它延缓了对象的释放,在有大量自动释放的对象时,会占用大量内存资源。因此,需要避免将大量对象自动释放。并且,在以下两种情况下,需要手动建立并手动销毁掉自动释放池。
   当在主线程外开启其他线程时,系统只会在主线程中自动生成并销毁自动释放池。
   在短时间内制造了大量自动释放对象时,及时销毁有助于有效利用iPad上有限的内存资源。

 

3.3 访问器方法与属性 Top

    当需要访问一个对象的成员变量时,不是直接访问这个成员变量,而是借助于访问器方法(accessor method),这样做的好处是帮助实现了代码的封装。可是,在内存管理相对复杂的环境中,要为一个变量实现访问器方法是一件非常烦琐的事情,如果手动写代码来实现所有的访问器方法,将会大大降低效率,并容易发生失误。如果使用一些通用的访问器方法在访问成员变量时帮助管理内存,那么将大大提高开发的效率并可防止内存泄漏的发生。
    在Objective-C中,可以很方便地为对象作出属性声明(property declaration),然后让编译器去合成(synthesize)对象的访问器方法。比如属性声明如下:
@property int value;
就等价于如下两个访问器方法的声明:
-(int)value;
-(void)setValue:(int)newValue;
而以下指令将告诉编译器去合成这两个访问器方法:
@synthesize value;
注意,编译器是按照@property后面跟随的属性来确定如何合成访问器方法的,因此为变量声明属性至关重要。下面介绍几组属性。
   可写性(Writability)
readwrite:指定这个属性会让编译器合成getter和setter;如果不指定readonly,那么readwrite将为默认属性。
readonly:指定这个属性会让编译器只合成getter,而不合成setter。在这种情况下,如果使用点语法(dot syntax)来给变量赋值,编译器会报错,提醒这个变量具有只读属性。
在定义读写属性时应当注意:对于外界只读取而不修改的变量,尽量使用readonly属性;外界毫不关心的变量,则不要为其设置任何属性。这样封装能增加代码的独立性和安全性。
   存入器属性(Setter Semantics)
assign:表示在setter中只使用简单的赋值,而不会发送retain消息给对象。这是默认属性。当变量类型为scalar时(比如int、CGRect),应当指定这个属性。
retain:表示在setter中将向旧值发送release消息,向新值发送retain消息。
copy:表示在setter中将向旧值发送release消息,向新值发送copy消息。

3.4 改变引用计数的特殊情况 Top

在iOS开发中,当使用外部接口(IBOutlet)时,应当使用如下属性声明:
@property (nonatomic,retain) IBOutlet IBObject* object;
以上模式只适用于iOS开发。针对MacOS开发,应当采用assign属性。
在nib文件(也就是xib文件)中的对象被创建时的引用计数为1,然后它们会被自动释放。随后,UIKit重建对象之间的结构,会用到可用的setter(如果没有setter的话,会自行retain对象),这样外部接口对象的引用计数依然为1,所以即使没有写代码来初始化它们或者保留它们,仍然需要在dealloc方法中释放它们(事实上,在前两章的例子中已经这样做了)。
在3.1和3.2节中提到,当一个对象被创建时引用计数为1,被retain时引用计数加1,被release时引用计数减1,被autorelease后引用计数减1。但这些不是会引起引用计数发生变化的全部情形。比如,Nib对象的使用会“悄悄”改变引用计数。事实上在其他一些特殊情况下,引用计数也会被改变。
q NSArray:当把一个对象添加到NSArray时,对象的引用计数会加1;反之当把对象从NSArray移除时,对象的引用计数会减1。同样的规则也适用于其子类NSMutableArray。
q UIView:当一个视图对象使用addGestureRecognizer:添加手势识别器时,手势识别器的引用计数会加1;反之使用removeGestureRecognizer:时,手势识别器的引用计数会减1。类似地,当视图对象使用addSubview:和removeFromSuperView时,对象的引用计数也会发生变化。
总结一下,容器(container)使用名字中带有“add”的方法常常会增加对象的引用计数,使用名字中带有“remove”的方法常常会减少引用计数。容器指的是数组(NSArray)、字典(NSDictionary)、集合(NSSet)等,它们用于集中管理大量对象。

3.5 内存管理总结 Top

下面来总结一下内存管理规则:
1)一个对象的引用计数为正时,它依然存在;当它的引用计数为0时,就被销毁。
2)一个对象通过alloc、create、copy和retain等方法成为另一个对象的所有者。
3)当一个所有者使用完一个对象之后,应当放弃对这个对象的所有权。放弃所有权的方法是发送release给这个对象,使得所有者对这个对象的引用计数的增减为0。
4)一个对象可能有多个所有者。这意味着,当一个所有者放弃所有权后,对象可能依然存在。只有当全部所有者放弃所有权时,对象才被销毁。
5)给对象发送autorelease消息,意味着这个对象的引用计数将在未来减1。
6)使用快捷方法创建对象后,不用给对象发送release消息。
7)使用属性声明,并且让编译器来合成访问器方法。
8)为外部接口作出nonatomic和retain的属性声明,并记得release它们。
9)一些容器(如NSArray和UIView的实例)会通过add方法增加对象的引用计数,通过remove方法减少对象的引用计数。
10)当自己开辟线程,或者建立大量临时的自动释放对象时,需要手动建立自动释放池并销毁它。
掌握了这些规则,再通过之后的实战加深对iOS内存管理的理解,就能够开发出高效率的iPad应用了!
  • 大小: 67.9 KB
  • 大小: 110.5 KB
  • 大小: 161.1 KB
  • 大小: 19.6 KB
  • 大小: 6.9 KB
  • 大小: 72.5 KB
  • 大小: 70.6 KB
  • 大小: 11.8 KB
  • 大小: 5 KB
  • 大小: 14.6 KB
  • 大小: 7.1 KB
  • 大小: 67.7 KB
  • 大小: 103.4 KB
  • 大小: 67.6 KB
  • 大小: 19 KB
  • 大小: 23.2 KB
  • 大小: 7.4 KB
  • 大小: 40.4 KB
  • 大小: 81.3 KB
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

文章信息

Global site tag (gtag.js) - Google Analytics