所谓入门,那肯定是从零开始。既然是开发,那么就要从开发的语言开始。作为学习新的开发,就我个人而言一般是这样的过程:1、开发环境的了解--->2、开发语言的学习(包括了开发工具的使用)--->3、摸索之路的iPhone开发(有关iPhoneSDK的熟悉)。完成以上三步,OK,就可以算差不多入门了,欠缺的就是火候问题。
在这里,我主要侧重的是第二步,开发语言的学习(其实第三步自己也没有实现)。Objective-C是进行iPhone开发的主要语言。本文将侧重于Objective-C。当然,任何一种开发语言都无法脱离于运行环境,Objective-C也不例外。所以在本系列当中也会介绍一些SDK里面的一些特性,比如说NSString, NSArray等等,比如在介绍代码的时候,就会引申一些Objective-C相应其他的知识,有助我们更全面的了解这门语言。OK,开始吧。
1、需要准备的东西:
第一,你需要一台苹果电脑。当然这个不是必需的条件,如果可以在PC上成功安装MAC OS的话,请忽略这一条。
第二,你需要去苹果网站上下载开发工具XCODE。注意,XCODE是免费的,但要和你安装的MAC OS相匹配。
2、Hello World!引发的知识。
先上代码如下:
1 #import <Foundation/Foundation.h>
2
3 int main (int argc, const char * argv[]) {
4 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
5
6 // insert code here
7 NSLog(@"Hello, World!");
8 [pool drain];
9 return 0;
10 }
真是老套,为什么大家学习语言都是从Hello,World!开始呢,谁知道呢,于是我也从这里开始了。
**有过C/C++或者java经验的同学们对第1行代码应该很熟悉了,导入代码中需要用到框架包。
引申知识:Foundation框架从属于Cocoa框架集,Cocoa的另外一个框架为Application Kit,或者是UIKit,其中前者的应用对象为MAC OS,后者的应用对象为iPhone OS。
**第3行主程序的入口都是main。这个main和C/C++语言里面的main是完全一样的,和java语言在本质上也是完全一样。因为 Objective-C完全的继承了C语言的特性。确切的说,不是说Objective-C和C语言很相似,而是Objective-C和C语言是完全兼容的。
@第7行的NSLog这个函数。这段代码的意思就是输出一个字符串,Xcode的代码生成器自己把字符串定义为“Hello, World!”。
引申知识:对NS的理解说明。NS其实只是一个前缀,为了避免命名上的冲突。NS来自于NeXTStep的一个软件,NeXT Software的缩写,NeXT Software是Cocoa的前身,一开始使用的是NS,为了保持兼容性所以NS一直得以保留。团队开发的时候最好事最好先定义好各自的前缀。但是,最好不要使用NS前缀,这样会让其他人产生误解。
**第4行是有关内存的处理方便的内容,刚才有说到Objective-C和C语言是完全兼容的,那么说到C语言,难免让人想到烦人的内存分配和回收的机制(这是java开发者值得庆幸的地方),下面说一下Autorelease和Pool。
引申知识:我们都知道,程序在执行的时候,需要向系统申请内存空间的,当内存空间不再被使用的时候,就需要被释放,否则有限的内存空间会很快被占用光光,后面的程序将无法得到执行的有效内存空间。在Objective-C或者说Cocoa里面,有三种内存的管理方式。
第一种,叫做“Garbage Collection”。这种方式和java类似,在你执行程序的过程中,不需要考虑什么时候申请内存空间,什么时候收回内存空间,背后总有一个“贤内助”会帮你考虑到并且处理好。但是这样的付出总是有代价的,需要消耗一定的资源,想想你购买和使用iPhone时候对于内存大小的纠结吧,在携带设备里面,资源是紧俏商品所以iPhone不支持这个功能。所以说“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将无法使用这段内存了。这种方式,尤其是在多线程的程序里面很重要,如果多个线程同时使用某一段内存的时候,安全的控制这些内存就显得尤为重要。
接下来,有关解释一下Autorelease方式。上述的alloc->retain->release->dealloc过程看起来比较令人满意,但是有的时候不是很方便,我们代码看起来会比较罗嗦,这个时候就需要Autorelease。顾名思义意为自动释放。但它不是立即把计数器减1而是把这个过程放在线程里面加以维护。当线程开始的时候,需要通知线程(NSAutoreleasePool),线程结束之后,才把这段内存释放(drain)。Cocoa把这个维护所有申请的内存的计数器的集合叫做pool,当不再需要pool(水池)的时候就要drain(放水)。
第三种,就是传统的C语言的方式,除非你写的都是C,否则不建议。
通过以上的内容,基本对Objective-C有了一个大概的了解,其中主要是理解内存的管理方法的思路。接下来让我们进一步了解Objective-C的其他东西吧。
3、完整的代码结构及特色的语法说明
简单来说,一个完整的Objective-C的代码分可以分为三个部分:1、头文件的定义(.h后缀);2、对头文件的实现文件(.m后缀);3、主程序文件或者叫main文件(.m后缀),当然这样的分类缺乏规范性,只是为了方便我们理解而已。接下来转录一些关于Objective-C的常见语法说明。
A、文件头的引用:使用#import "文件名" 或者 #import<文件名>的形式确保每个头文件仅被包含一次。
B、类声明及实现:声明以 @interface 类名:继承类 开头,以 @end 结尾;实现以 @implementation 类名 开头,以 @end 结尾。
C、实例方法:即成员方法,在方法名前添加一个减号(-),如java中用来set/get等;类方法,在方法名前添加一个加号(+)。如java中Object中定义的方法。
D、类方法的调用:格式为[类名 类方法],成员方法调用格式为[实例名 实例方法],这种模式被称为消息机制,格式为[对象 消息]即给对象发送一个消息。
我们以一个简单的例子来说明这样的方式:
代码如下:
“Car.h”文件:
1 #import <Foundation/Foundation.h>//头文件的引用
2
3
4 @interface Car: NSObject {//Objective-C中所有类都继承NSObject 类,和java中的Object类相似。
5 int tiresCount;//成员属性的声明
6 }
7 - (void)haveSomething;//成员方法
8 - (void)setTiresCount:(int) count;
9 @end
**其中第8行,有个“:(int) count”。这里冒号放在方法的后面是用来表示后面是用来定义变量的,同样变量的类型使用括号给包住,其中的count,就是变量的名字。如果有好多几个变量怎么办呢?答案就是在第一个变量后面加冒号,然后加括号包住变量的类型,接着是变量的名字。
引申知识:我们看到第4行和第9行出现了“@”符号。这到底是什么东西呢?其实这也没有什么神秘的,我们可叫它编译器向导,是为了和C语言的字符串定义区别开来,如Java和C++定义了关键字class用于声明一个类,在Objective-C里面,确不存在这样的关键字。类的定义就规定从@interface开始到@end结束,即编译器看到了@interface就知道了这是类的定义的开始,看到了@end就知道,类的定义结束了,仅此而已。
“Car.m”文件:
1 #import "Car.h"
2
3
4 @implementation Car
5 -(void) haveSomething//成员方法的实现
6 {
7 NSLog(@"Hello,My name is Car, I have %d tires.", tiresCount);
8 }
9 -(void) setTiresCount:(int) count
10 {
11 tiresCount= count;
12 }
13 @end
**第5行定义的haveSomething的实现。
**第7行和之前的Hello, World输出有些相似,只不过多了一个%d,还有实体变量tiresCount,这和C语言里面的printf类似,输出的时候会使用tiresCount来替代%d。
**第11行,把参数count的值赋值给实体变量tiresCount。相当于java里面的set方法。
“CarMain.m”文件:
1 #import <Foundation/Foundation.h>
2 #import "Car.h"
3
4 int main (int argc, const char * argv[]) {
5 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
6
7 id car= [Car new]; //类的实例化
8 [car setTiresCount:4];//成员方法的调用,将car的轮胎数量设为4
9 [car haveSomething];//成员方法的调用
10
11 [pool drain];
12 return 0;
13 }
**这里我们关键讲讲第7行,这里出现了id以及[Car new]。
引申知识:[Car new]是创建对象,new实际上是alloc和init的组合,和其他的语言一样,创建对象就是为这个对象分配内存并且初始化的过程。顺便说一声new,alloc还有init定义在Car的超类NSObject里面。
引申知识:id是英文identifier的缩写,Objective-C中使用id来代表一个对象,可以说所有的对象都可用id来进行区分。我们知道一个类仅仅是一些数据外加上操作这些数据的代码,所以id实际上就是指向数据结构的一个指针,相当于void*。
好了,通过上述的一些描述,相信我们基本可以了解Objective-C的结构,我们在这里总结一下,类的定义方法如下:
@interface 类名 : 父类名 {
变量类型 变量名;
……
}
- (返回值类型)方法名字;
+ (返回值类型)方法名字;
- (返回值类型)方法名字:(变量类型) 变量名字 标签1:(变量类型) 变量1名字;
@end
类的定义部分的语法:
@implementation 类的名字
-(方法返回值) 方法名字
{
方法定义
……
}
-(方法返回值) 方法名字:(变量类型) 变量名字
{
方法定义
……
}
@end