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.

 

待续。。。。

arrow
arrow
    全站熱搜

    NatPixnet 發表在 痞客邦 留言(0) 人氣()