copy from: http://blog.sina.com.cn/s/blog_5df7dcaf0100be6w.html~type=v5_one&label=rela_nextarticle
从前,一个叫Brad Cox的人觉得是时候让编程更模块化一些.当时C语言是非常流行和强大的语言,Smalltalk 是一个优雅的面向对象语言.所以,基于C,Brad Cox增加了类Smalltalk的类和消息发送机制, Objective-C语言就诞生了.
Objective-C不是私有语言.相反,它做为一个开放标准被GNU C(gcc)所包含很多年了. Cocoa是用Objective-C开发的,大部分Cocoa开发也是使用Objective-C语言.
讲解c和面向对象基础需要用一整本书.相对于写一本书,这一章会简单介绍Objective-C语言.如果你有一定的C和面向对象基础,学习起来不会很难.我也建议你学习apple的Objective-C Language这本书.
创建,使用对象
使用已有的类
创建自己的类
调试器
你做了什么
思考: 消息机制工作原理是什么?
挑战
创建,使用对象
在第一章有提到:类是用来创建对象,对象有自己的方法.而你可以给对象发送消息,让它执行特定的方法.这一节中,我们就来学习怎么创建对象,并给它们发送消息,在不再需要使用它们时怎么释放它们.
我们以NSMutableArray作为例子.你可以创建一个NSMutableArray的对象,并发送消息 alloc给NSMutableArray类. 就像这样 [可能你有疑问: 我到底可以给谁发送消息啊? 这里好像是给类NSMutableArray发送alloc消息阿.这是怎么回事?其实你可以把NSMutableArray类当作一个对象看待.Objective-C Language有比较好得简介.有机会计划把它也给翻译了]
[NSMutableArray alloc];
该方法返回一个指针,指向为一个对象分配好的空间.你可以使用一个变量来表示它,如
NSMutableArray *foo;
foo = [NSMutableArray alloc];
面对Objective-C时,记住,foo只是一个指针变量,在这里它指向一个对象[好好琢磨一下这句话]
在使用foo指向的对象之前,我们需要对它进行初始化. 发送init消息来完成
NSMutableArray *foo;
foo = [NSMutableArray alloc];
[foo init];
好好琢磨一下最后一行代码. 发送init消息给foo所指向的对象.也就是"foo是消息init的接收者". 我们也可以给一个类发送消息.就像给NSMutableArray发送alloc一样.
init发送会返回一个新的初始化好得对象,我们经常会这要写:
NSMutableArray *foo;
foo = [[NSMutableArray alloc] init];
怎么释放对象呢?在第四章我们会介绍(包含所有和NSAutoreleasePool的知识)
有些方法可能接受参数. 这是方法的名字(也叫selector)将以:号结束. 例如,给array的最后添加一个对象我们使用addObject: 方法(假定bar是一个对象指针)
[foo addObject:bar];
如果接受多个参数,那么selector 由几个部分组成.例如,在特定的索引处添加对象:
[foo insertObject:bar atIndex:5];
请注意 intsertObject:atIndex 是一个selector.它会触发有两个输入参数的方法.你可能觉得这和C或是Java太不一样了.但是它是Smalltalk风格.这样的语法是你的更容易理解和阅读.例如,我们看看一个C++的调用
if (x.intersectsArc(35.0, 19.0, 23.0, 90.0, 120.0))
象比较而言,Objective-C更容易猜到函数的意义
if ([x intersectsArcWithRadius:35.0
centeredAtX:19.0
Y:23.0
fromAngle:90.0
toAngle:120.0])
如果你刚开始觉得别扭,不奇怪,先试着用用.大多数的程序员会喜欢上这种语法风格[我也是]
现在你大概可以理解一些Objective-C代码了.让我们来编写一个程序来创建一个NSMutableArray对象.并给它添加10个NSNumber对象吧
使用已有的类
请再次打开XCode,关掉之前的工程. 在File菜单下选择New Project... 在弹出的面板中选择Foundation Tool (图3.1)
说明一些Foundation Tool. 这个工程模板创建没有用户界面的程序.在后台无用户交互的运行.也就是Command Line程序.我们通常需要修改它的main函数.
把工程命名为lottery.不要其他应用程序.Command Line Tool一般以小写字母命名.如图3.2
在Source下面选择lottery.m. 如下编辑lottery.m
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *array;
array = [[NSMutableArray alloc] init];
int i;
for (i = 0; i < 10; i++) {
NSNumber *newNumber = [[NSNumber alloc] initWithInt:(i * 3)];
[array addObject:newNumber];
}
for ( i = 0; i < 10; i++) {
NSNumber *numberToPrint = [array objectAtIndex:i];
NSLog(@"The number at index %d is %@", i, numberToPrint);
}
[pool drain];
return 0;
}
让我们逐行逐句解析一下
#import <Foundation/Foundation.h>
包含了整个Foundation Framework[还记得它吧.Cocoa 3个framework之一.]
int main (int argc, const char *argv[])
象你看到的其他Unix C程序一样
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
声明了指针变量 pool.指向一个新创建的NSAutoreleasePool对象.我们会在下一章详细讨论autorelease pool
NSMutableArray *array;
声明了一个指针变量array. 指向一个NSmutableArray对象. 注意这是还没有任何array对象存在.你只是声明了一个将会指向NSmutableArray对象的指针
array = [[NSMutableArray alloc] init];
创建了一个NSmutableArray对象,并让array指向它.
for (i = 0; i < 10; i++) {
NSNumber *newNumber = [[NSNumber alloc] initWithInt:(i*3)];
[array addObject:newNumber];
}
在for循环中,创建了临时指针变量newNumber,并指向新创建的NSNumber对象. 同时把NSNumber对象添加到array中去.
这个array并没有拷贝这些NSNumber对象.它只是保留了这些NSNumber对象的指针. Objective-C程序员很少做对象拷贝,因为很少有这种需要. [可以看看Objective- C的内存管理机制,后面有介绍为什么]
for ( i = 0; i < 10; i++) {
NSNumber *numberToPrint = [array objectAtIndex:i];
NSLog(@"The number at index %d is %@", i, numberToPrint);
}
这里会把array中的NSNumber打印输出到console. NSLog类似C语言中的printf()函数.并且会在开头打印出应用程序的名字和当前时间.
对于printf,你可能会用%x去让一个整型数打印成16进制格式. NSLog函数同样也支持. 并且它还可以支持%@来打印一个对象类型. 当使用%@时,它会给对象发送消息description, description会返回一个string来代替%@.我们马上就会讨论到description方法
表3.1
是NSLog()支持的符号(token)
***************************************************************************
注意: 如果"The number at index %d is %@"之前的@看起来是有点奇怪.请记住,Objective-C是C语言的扩展.其中一个扩展就是对Objective-C的字符窜可以是NSString的对象.对于C, 字符窜就是存储了字符的以null字符结束的连续内存块. 区分C字符窜常量和Objective-C字符串常量,就是看前面有没有@. 如
// C string
char *foo;
// NSString
NSString *bar;
foo = "this is a C string";
bar = @"this is an NSString";
你应该会经常用到NSString. 当然如果你有很多c函数使用到c字符串也是可以的.
你可以把这两种字符串进行转换
const char *foo = "Blah blah";
NSString *bar;
// Create an NSString from a C string
bar = [NSString stringWithUTF8String:foo];
// Create a C string from an NSString
foo = [bar UTF8String];
因为NSString可以是Unicode的字符串.所以你要正确的处理C字符串的多字节的字符,通常这有些难且废时.(处理多字节字符问题.你可能还会遇到有些语言是从右向左读这样的问题) 不管怎么样,尽量用NSString代替C字符串
***************************************************************************
我们继续看代码
[pool drain];
return 0;
}
这里的autorelease pool,我们将在下一章讨论.
在工具条上,你可以找到一个pop-up菜单: Active Build Configuration. 它有两个选项: Debug 和 Release.当你在开发阶段,你应该会使用Debug. 如果要发布你的程序,你将会有Release. 它们有什么区别么? Release会创建UB,并且去掉了debug说用得的symbol.所以,Release设置大概需要2倍时间来编译,而且不能做debug了. [为了完成书中的例子,这里只是对XCode做了一些简单的介绍.读者应该找XCode帮助,详细了解XCode开发工具]
点击Build and Go (图3.3)
(如果你没有看到的console, 点击Run->Console菜单)
-- 给nil (空对象)发送消息
对于很多的面向对象语言,如果你给nil发送消息当导致程序的崩溃.使用那些语言你可能会用到很多的非空检查. 例如在Java中看到
if (foo != null) {
foo.doThatThingYouDo();
}
在Objective-C中.给nil发送消息不会有什么问题. 这个消息被忽略而已.这样就消除了太多的非空检查.例如下面的代码可以正常的编译运行
id foo;
foo = nil;
int bar = [foo count];
这虽然和大多数语言不同.不过我们还是会这样使用
-- NSObject, NSArray, NSMutableArray, NSString
不错,到现在我们用到了C噢出噢阿提供的类:NSObject,NSMutableArray,NSString.(Cocoa所有的类都有NS的前缀,建议你自己定义类时不要使用NS的前缀了.)这些类都包含在Foundation framework里面. 图3.4展示了它们的继承关系.
现在让我们一起来看看这些类中常用到的方法吧. 你可以在XCode help 中的在线帮助文档里找到完整的函数说明
-NSObject
NSObject是所有Objective-C类的根类.以下是它的常用方法的说明
- (id)init
在分配内存空间后对receiver进行初始化. init一般和alloc方法写在一起. 如:
TheClass *newObject = [[TheClass alloc] init];
- (NSString *)description
返回一个NSString对象,描述receiver.如果你在debugger上(gdb)上使用"po" (print object),就会调用这个方法.如果设计好description方法,可以更容易的来做debug. 同样,你使用%@,也会调用该方法.如之前在main函数中看到的:
NSLog(@"The number at index %d is %@", i, numberToPrint);
它等价于:
NSLog(@"The number at index %d is %@", i,
[numberToPrint description]);
- (BOOL)isEqual:(id)anObject
当receiver和anObject相等时返回YES.否则返回NO. 一般你会这样用:
if ([myObject isEqual:anotherObject]) {
NSLog(@"They are equal.");
}
这里有个疑问了?到底什么是相等呢?对于NSObject来说, 这个方法定义只用receiver和anObject为同一个对象是相等.也就是它俩指向同一个内存地址.
当然,并不是所以的类都想定义这样的相等.所以很多的类会重载这个方法来实现自己的相等策略.比如:NSString就重载了这个方法,比较receiver和anObject的字符是否相等. 如果它们的字符和字符排列顺序都相等是返回YES.
所以,对于两个NSString对象 x 和 y.下面两个表达式是有不同意义的
x == y
和
[x isEqual:y]
第一个表达式是比较两个指针是否相等.而第二个是比较两个指针所指向的NSString对象是否相等. 那如果x和y都是的类没有重载NSObject的 isEqual:方法,这两个表达式的结果是一样的.
- NSArray
一个NSArray对象包含指向其他对象的指针列表. 所有指针有一个唯一的index. 例如, 有n个对象指针,那么index从0 - (n-1).你不能把nil增加到NSArray中(这就意味这NSArray中没有"空洞",这和Java的Object[]不一样). NSArray从NSObject继承而来
NSArray在创建的时候,就包含了所有对象.你不能增加或是删除其中任何一个对象.这种特定称为: immutable(NSArray的子类NSMutableArray是mutable的.我们稍后讨论.)这种不能改变的对象,在某些情况下是非常有用的: 很多对象可以共享一个NSArray,而不会担心谁会修改了这个共享NSArray对象. NSString和NSNumber也是这样的immutable对象. 如果你想要一个新的string或number,不用修改原来的NSString和NSNumber.直接重新创建新的NSString和NSNumber就好了(NSString有对应的mutable类,NSMutableString来允许修改其中的字符)
看看NSArray常用的方法:
- (unsigned)count
得到array中的对象个数
-(id)objectAtIndex:(unsigned)i
得到索引为i的对象.如果i值超过了array对象数量,在程序运行到这里会产生错误
-(id)lastObject
得到最后一个对象.如果NSArray中没有任何对象存在,返回nil.
-(BOOL)containsObject:(id)anObject
当anObject出现在NSArray中,则返回YES. 对于出现的定义是这样的: NSArray会调用对象的isEqual:方法,并把anObject当成参数. 如果isEqual:返回YES,那么说明anObject出现.
-(unsigned)indexOfObject:(id)anObject
查找NSArray中是否存在anObject, 并返回最小的索引值. 同样会调用对象的isEqual:方法作为比较函数.如果没有找到则会返回NSNotFound. [一个NSArray可能有3个NSString对象. 而所以的NSString都是@"hello" , 这是调用 indexOfObject:@"hello" , 那么会找到3个,返回的回事最小的索引0]
--NSMutableArray
NSMutableArray继承NSArray,扩展了增加,删除对象的功能. 可以使用NSArray的mutableCopy方法来复制得到一个可修改的NSMutableArray对象.
看看它常用的方法
- (void)addObject:(id)anObject
在reciever最后添加anObject. 添加nil是非法的
- (void)addObjectsFromArray:(NSArray *)otherArray
在reciever最后,把otherArray中的对象都依次添加进去.
- (void)insertObject:(id)anObject atIndex:(unsigned)index
在索引index处插入对象anObject. 如果index被占用,会把之后的object向后移动. index不能大约所包含对象个数,并且anObject不能为空.
- (void)removeAllObjects
清空array.
- (void)removeObject:(id)anObject
删除所有和anObject相等的对象.同样使用isEqual:作为相等比较方法.
- (void)removeObjectAtIndex:(unsigned)index
删除索引为index的对象.后面的对象依次往前移.如果index越界,将会产生错误
如上所说,我们不能把nil加到array中. 但是有的时候我们真的想给array加一个空的对象.这时可以使用NSNull来做这件事.如:
[myArray addObject:[NSNull null]];
--NSString
一个NSString对象可以存储一段Unicode字符.在Cocoa中.所以和字符,字窜相关的处理都是使用NSString来完成.而Objective-C也支持@"...."这样的格式来定义7-bit ASCII 编码的字符串常量
NSString *temp = @"this is a constant string";
NSString继承至NSObject.我们来看看它的一些常用方法
-(id)initWithFormat:(NSString *)format, ...
就像sprintf. format由很多记号组成.比如%d. 其他参数就是这些记号的替代:
int x = 5;
char *y = "abc";
id z = @"123";
NSString *aString = [[NSString alloc] initWithFormat:
@"The int %d, the C String %s, and the NSString %@",
x, y, z];
- (unsigned int)length
返回字符个数
- (NSString *)stringByAppendingString:(NSString *)aString
给一个字符串附加一个字符串aString.如下面的代码,产生字符串:"Error:unable to read file"
NSString *errorTag = @"Error: ";
NSString *errorString = @"unable to read file.";
NSString *errorMessage;
errorMessage = [errorTag stringByAppendingString:errorString];
--继承和组合
新手的cocoa 程序员经常愿意创建NSString,NSMutableArray创建自己的子类.不要这样做!现在的Objective-C程序员基本从来不这样做.他们会让一个较大的对象来包含NString或NSMutableArray,也叫做对象组合. 举个例子: BankAccount可能会声明成NSMutableArray的子类.毕竟, 银行帐号不就是一些交易处理的集合吗?新手很容易会这样做. 而大牛们通常会让BankAccount继承NSObject, 并且声明一个成员变量transactions来指向一个NSMtableArray对象.
明白 有一个 和 是一个 两者之间的区别很重要. 新人经常会说"BankAccount 是继承NSMutableArray". 而大牛会说"BankAccount 使用到了 NSMutableArray".Objective-C基本编程思想是:通常使用 有一个,而不是是一个
你会发现,使用一个类往往比继承一个类简单的多. 继承类需要编写更多的代码,也要比较深入了解父类. 但是使用组合,Cocoa开发者可以应用到很多强大的类,而不需要知道那些类的实现细节.
对于强制类型语言,比如C++. 继承是非常重要的. 而对于非强制类型语言,如Objective-C. 继承就不是那么重要了.在本书中只有两处会出现继承关系.而大部分都是一写对象包含组合关系.认清这点,对于一个Cocoa程序员尤其重要.
创建自己的类
在我生活的城市,政府出台了彩票政策,容许大家每周都可以购买彩票.想象一下,我们要编写一个程序来生成彩票.每张彩票有两个数字,1到100之间. 我们需要为下十个星期制作彩票. 每个LotteryEntry对象有时间和两个数字.如图3.5.
--创建LotteryEntry类
首先创建文件.在File菜单上选择New file... 选择Objective-C class. 文件命名为LotteryEntry.m (图3.6)
注意,LotteryEntry.h也同时被创建了
-- LotteryEntry.h
编辑LotteryEntry.h如下:
#import <Foundation/Foundation.h>
@interface LotteryEntry : NSObject {
NSCalendarDate *entryDate;
int firstNumber;
int secondNumber;
}
- (void)prepareRandomNumbers;
- (void)setEntryDate:(NSCalendarDate *)date;
- (NSCalendarDate *)entryDate;
- (int)firstNumber;
- (int)secondNumber;
@end
在辑LotteryEntry.h中我们声明了一个新类LotteryEntry. 它继承至NSObject. 包含3个成员变量entryDate, firstNumber,和 secondNumber.
. entryDate 的类型是NSCalendarDate
. firstNumber, secondNumber的类型是int.
待续。。。。