1、本文适合人群
如果你对 iPhone 感兴趣, 如果你考虑向全世界的人们展示你的创意, 如果你有一颗好奇心,
如果你打算通过开发 iPhone 程序谋生, 如果你觉得苹果比 Windows 酷, 如果你认为不懂苹果的
话那么就有些不时尚的话,那么可以考虑阅读本系列。
老手也可以考虑花一点时间阅读一下,可以发帖子和笔者交流切磋。笔者发布的文章属于
公益写作,旨在为大家介绍 iPhone 开发的一些基础知识,如果可以提供宝贵意见,笔者将不胜
感激。
2、工欲善其事必先利其器《论语·魏灵公》
第一,你需要一台苹果电脑。当然这个不是必需的条件,如果你可以在你的 Intel PC 上成
功安装 MAC OS 的话,那么请忽略这一条。
第二, 你需要去苹果网站上下载开发工具 XCODE。 注意, XCODE 是完全免费的, 但是需
要你去注册一个账号才可以下载。 由于 XCODE 不时的在更新,所以如果你的 MAC OS 不支持
你下载的 XCODE 的话,那么你也许需要考虑买一个最新的 MAC OS。
第三,你需要至少有 C,C++,或者 JAVA 的背景知识。不过如果你没有,那么也不用担心,
相信阅读了笔者的文章之后应该也可以掌握。
最后需要的东西就不是必须的了,当然有的话会更好一些。这些东西是,开发者账户(需
要付费) , iPhone 手机(在部分国家可以免费获得, 但是中国会怎么样, 笔者不是很清楚) , iPod
Touch(需要购买) 。
2.1、从hello word开始(怎样构建hello word)
第一步, 启动 Xcode。 初次启动的时候, 也许会弹出一个“ Welcome to Xcode”的一个对话框。
这个对话框和我们的主题没有关系,我们可以把它关掉。
第二步,选择屏幕上部菜单的“ File->New Project” ,出现了一个让你选择项目种类的对话框。
你需要在对话框的左边选择“Command Line Utility” , 然后在右边选择“ Foundation Tool”,然后选
择“ Choose...”按钮。(开发Object-c首选Xcode)
注:本文将不再讲解Xcode使用方法,Xcode使用方法很好找,在网上一搜一大堆,故在此不再讲解。
看下面这段代码:
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// insert code here
NSLog(@"Hello, World!");
[pool drain];
return 0;10 }
有过 C/C++经验的同学看到第一行, 也许会觉得有些亲切; 有过 Java 经验的同学看到第一
行也许也会有一种似曾相识的感觉。同学们也许猜到了这是干什么用的,没错,这个正是头文
件。不过,在 C/C++里面是#include,在 java 里面是 import,这里是#import。
在 C/C++ 里面会有#include 互相包含的问题,这个时候需要 #ifdef 来进行编译的导向, 在
Xcode 里面, 同学们可以"放心的"包含各种东西, 这个没有关系, 因为我们的编译器有足够的“ 聪
明”, 因为同一个头文件只是被导入一次。 除了#import 变得聪明了一点之外, 和#include 的功能
是完全一样的。
我们再来看看我们的另外一个新的朋友 ---Foundation.h 。这个是系统框架 Foundation
framework 的头文件, 有了它你可以免费的获取系统或者说苹果公司为你精心准备的一系列方便
你使用的系统功能,比如说字符串操作等等。Foundation 框架从属于 Cocoa 框架集,Cocoa 的
另外一个框架为 Application Kit,或者是 UIKit,其中前者的应用对象为 MAC OS,后者的应用
对象为 iPhone OS 。
2.1.1 、main 函数
有过 C/C++或者 java 经验的同学们对第 3 行代码应该很熟悉了,是的大家都一样主程序的
入口都是 main。 这个 main 和 C/C++语言里面的 main 是完全一样的, 和 java 语言在本质上也是
完全一样的。因为 Objective-C 完全的继承了 C 语言的特性。确切的说,不是说 Objective-C 和
C 语言很相似,而是 Objective-C 和 C 语言是完全兼容的。
2.1.2关于 NSAutoreleasePool
在第 4 行,我们遇到了另外一个新鲜的东西,这就是 NSAutoreleasePool。
让我把这个单词分为三部分,NS,Autorelease 和 Pool。
当我们看到 NS 的时候, 也许不知道是代表着什么东西。 NS 其实只是一个前缀,为了避免
命名上的冲突。NS 来自于 NeXTStep 的一个软件,NeXT Software 的缩写, NeXT Software 是
Cocoa 的前身,一开始使用的是 NS,为了保持兼容性所以 NS 一直得以保留。在多人开发的时
候,为了避免命名上的冲突,开发组的成员最好事先定义好各自的前缀。但是,最好不要有同
学使用 NS 前缀,这样会让其他人产生误解。
略微有些遗憾的是, Objective-C 不支持 namespace 关键字, 不知道后续的版本是否会支持。
下面我们讨论一下 Autorelease 和 Pool。
程序在执行的时候,需要向系统申请内存空间的,当内存空间不再被使用的时候,毫无疑
问内存需要被释放,否则有限的内存空间会很快被占用光光,后面的程序将无法得到执行的有
效内存空间。从计算机技术诞生以来,无数的程序员,我们的无数先辈都在为管理内存进行努
力的工作,发展到现在,管理内存的工作已经得到了非常大的完善。
在 Objective-C 或者说 Cocoa 里面,有三种内存的管理方式。
第一种,叫做“Garbage Collection”。这种方式和 java 类似,在你的程序的执行过程中,始
终有一个高人在背后准确地帮你收拾垃圾,你不用考虑它什么时候开始工作,怎样工作。你只
需要明白,我申请了一段内存空间,当我不再使用从而这段内存成为垃圾的时候,我就彻底的
把它忘记掉,反正那个高人会帮我收拾垃圾。遗憾的是,那个高人需要消耗一定的资源,在携
带设备里面,资源是紧俏商品所以 iPhone 不支持这个功能。所以“ Garbage Collection ”不是本入
门指南的范围,对“ Garbage Collection ”内部机制感兴趣的同学可以参考一些其他的资料,不过
说老实话“ Garbage Collection”不大适合适初学者研究。
第二种, 叫做“ Reference Counted”。 就是说, 从一段内存被申请之后, 就存在一个变量用于
保存这段内存被使用的次数,我们暂时把它称为计数器,当计数器变为 0 的时候,那么就是释
放这段内存的时候。比如说,当在程序 A 里面一段内存被成功申请完成之后,那么这个计数器
就从 0 变成 1 (我们把这个过程叫做 alloc) , 然后程序 B 也需要使用这个内存, 那么计数器就从
1 变成了 2(我们把这个过程叫做 retain ) 。紧接着程序 A 不再需要这段内存了,那么程序 A 就
把这个计数器减 1 (我们把这个过程叫做 release) ; 程序 B 也不再需要这段内存的时候, 那么也
把计数器减 1(这个过程还是 release) 。当系统(也就是 Foundation)发现这个计数器变成了 0, 那
么就会调用内存回收程序把这段内存回收 (我们把这个过程叫做 dealloc) 。 顺便提一句, 如果没
有 Foundation,那么维护计数器,释放内存等等工作需要你手工来完成。
这样做, 有一个明显的好处就是, 当我们不知道是 A 先不使用这段内存, 还是 B 先不使用
这段内存的时候,我们也可以非常简单的控制内存。否则,当我们在程序 A 里面释放内存的时
候, 还需要看看程序 B 是否还在使用这段内存, 否则我们在程序 A 里面释放了内存之后, 可怜
的程序 B 将无法使用这段内存了。这种方式,尤其是在多线程的程序里面很重要,如果多个线
程同时使用某一段内存的时候,安全的控制这些内存成为很多天才的程序员的梦魇。
如果有同学搞过 COM 的话,那么应该对 Release/AddRef 很熟悉了,其实 Obejctive-C 和他们
的机制是一样的。
接下来, 我需要解释一下 Autorelease 方式。 上述的 alloc->retain->release->dealloc 过程看起
来比较令人满意,但是有的时候不是很方便,我们代码看起来会比较罗嗦,这个时候就需 要
Autorelease。Autorelease 的意思是,不是立即把计数器减 1 而是把这个过程放在线程里面加以
维护。当线程开始的时候,需要通知线程 (NSAutoreleasePool) ,线程结束之后,才把这段内存
释放 (drain) 。 Cocoa 把这个维护所有申请的内存的计数器的集合叫做 pool, 当不再需要 pool( 水
池)的时候就要 drain(放水) 。
笔者想要说的是,虽然 iPhone 支持 Autorelease 但是我们最好不要使用。因为 Autoreleas e
方式从本质上来说是一种延迟释放内存的机制,手机的空间容量有限,我们必须节约内存,确
定不需要的内存应该赶快释放掉,否则当你的程序使用很多内存的情况下也许会发生溢出。这
一个习惯最好从刚刚开始学习使用 Objective-C 的时候就养成,否则长时间使用 Autorelease 会
让你变得“懒散”,万一遇到问题的时候,解决起来会非常耗费时间的。所以,还是关于内存管
理,我们还是自己动手,丰衣足食。当然笔者不是说绝对不可以使用,而是当使用 Autorelease
可以明显减低程序复杂度和易读性的时候,还是考虑使用一下换一下口味。
说到这里,可能有的同学已经开始发晕了,认为这个东西比较难以理解。是的,笔者在这
里只是介绍一个大概的东西,在这里只要了解计数器的概念就可以了,笔者将在随后的章节里
面对这个功能加以详细论述,请同学们放心,这个东西和 Hello World 一样简单。
关于 Pool
在使用 Pool 的时候,也要记住系统给你的 Pool 的容量不是无限大的,从这一点来说和在
现实世界的信用卡比较相似,你可以在一定程度透支,但是如果“忘记掉”信用卡的额度的话,会造成很大的系统风险。
第三种, 就是传统而又原始的 C 语言的方式, 笔者就不在这里叙述了。 除非你在 Objective-C
里面使用 C 代码,否则不要使用 C 的方式来申请和释放内存,这样会增加程序的复杂度。
线程是什么东西?线程指的是进程中一个单一顺序的控制流。它是系统独立调度和分派的
基本单位。同一进程中的多个线程将共享该进程中的全部系统资源,比如文件描述符和信号处
理等等。 一个进程可以有很多线程,每个线程并行执行不同的任务。
2.1.3关于[[NSAutoreleasePool alloc] init];
关于程序第 4 行等号右边出现的括弧以及括弧里面的内容, 笔者将在后续的章节里面介绍。
在这里, 同学们可以理解为, 通过告诉 Objective-C 编译器[[NSAutoreleasePool alloc] init] , 编译
器就会成功的编译生成 NSAutoreleasePoo 对象的代码就可以了。
2.1.4Objective-C 里面的注释
同学们在第 6 行看到了//的注释,这个和 C++ 以及 Java 是一样的,表示这一行的内容是注
释,编译器将会忽略这一行的内容。笔者在上面说过 Objective-C 完全兼容 C 语言,所以 C 语
言里面传统的/**/在 Objective-C 里面也是有效的。
2.1.5命令行输出
第 7 行, 我们看到了 NSLog 这个函数。 NS 上面已经讲过了, 我们都知道 Log 是什么意思,
那么这段代码的意思就是输出一个字符串, Xcode 的代码生成器自己把字符串定义为“ Hello,
World!”。 NSLog 相当于 C 语言里面的 printf, 由于我们是在使用 Objective-C 所以笔者将会和同
学们一起,在这里暂时忘记掉我们过去曾经熟悉的 printf 。
有眼光锐利的同学会发现在字符串的前面多了一个@符号,这是一个什么东西呢?
如前所述,Objective-C 和 C 是完全兼容的,但是 NSLog 这个函数需要的参数是 NSString,
这样就产生了一个问题, 如果使用 C 的字符串方式的话, 为了保持和 C 的兼容性编译器将会把
字符串理解为 C 的字符串。为了和 C 的字符串划清界限,在 C 的字符串前面加上 @符号,
Objective-C 的编译器会认为这是一个 NSString,是一个 NSLog 喜欢的参数。
为什么 NSLog 或者 Cocoa 喜欢使用 NSString? 因为 NSString 封装了一系列的字符串的方
法比如字符串比较,字符串和数字相互转换等等的方法,使用起来要比 C 的字符串方便的多。