Objective-C学习大纲 一

本文为台湾出版的《Objective-C学习大纲》的翻译文档,系统介绍了Objective-C代码,很多名词为台湾同胞特指词汇,在学习时仔细研读才能体会。

AD:

所有这篇初学者指南的塬始码都可以由 objc.tar.gz 下载。这篇教学中的许多範例都是由 Steve Kochan 在 Programming in Objective-C. 一书中撰写。如果你想得到更多详细资讯及範例,请直接参考该书。这个网站上登载的所有範例皆经过他的允许,所以请勿复製转载。

设定环境

Linux/FreeBSD: 安装 GNUStep

为了编译 GNUstep 应用程式,必须先执行位于 /usr/GNUstep/System/Makefiles/GNUstep.sh 的 GNUstep.sh 这个档案。这个路径取决于你的系统环境,有些是在 /usr, some /usr/lib,有些是 /usr/local。如果你的 shell 是以 csh/tcsh 为基础的 shell,则应该改用 GNUStep.csh。建议把这个指令放在 .bashrc 或 .cshrc 中。

Mac OS X: 安装 XCode

Windows NT 5.X: 安装 cygwin 或 mingw,然后安装 GNUStep

前言

这篇教学假设你已经有一些基本的 C 语言知识,包括 C 资料型别、什么是函式、什么是回传值、关于指标的知识以及基本的 C 语言记忆体管理。如果您没有这些背景知识,我非常建议你读一读 K&R 的书:The C Programming Language(译注:台湾出版书名为 C 程式语言第二版)这是 C 语言的设计者所写的书。

Objective-C,是 C 的衍生语言,继承了所有 C 语言的特性。是有一些例外,但是它们不是继承于 C 的语言特性本身。

nil:在 C/C++ 你或许曾使用过 NULL,而在 Objective-C 中则是 nil。不同之处是你可以传递讯息给 nil(例如 [nil message];),这是完全合法的,然而你却不能对 NULL 如法炮製。

BOOL:C 没有正式的布林型别,而在 Objective-C 中也不是「真的」有。它是包含在 Foundation classes(基本类别库)中(即 import NSObject.h;nil 也是包括在这个标头档内)。BOOL 在 Objective-C 中有两种型态:YES 或 NO,而不是 TRUE 或 FALSE。

#import vs #include:就如同你在 hello world 範例中看到的,我们使用了 #import。#import 由 gcc 编译器支援。我并不建议使用 #include,#import 基本上跟 .h 档头尾的 #ifndef #define #endif 相同。许多程式员们都同意,使用这些东西这是十分愚蠢的。无论如何,使用 #import 就对了。这样不但可以避免麻烦,而且万一有一天 gcc 把它拿掉了,将会有足够的 Objective-C 程式员可以坚持保留它或是将它放回来。偷偷告诉你,Apple 在它们官方的程式码中也使用了 #import。所以万一有一天这种事真的发生,不难预料 Apple 将会提供一个支援 #import 的 gcc 分支版本。

在 Objective-C 中, method 及 message 这两个字是可以互换的。不过 messages 拥有特别的特性,一个 message 可以动态的转送给另一个物件。在 Objective-C 中,唿叫物件上的一个讯息并不一定表示物件真的会实作这个讯息,而是物件知道如何以某种方式去实作它,或是转送给知道如何实作的物件。

编译 hello world

   
  1. hello.m
  2. #import
  3. intmain(intargc,constchar*argv[]){
  4. printf("helloworld\n");
  5. return0;
  6. }

输出

helloworld

在Objective-C中使用#import代替#include

Objective-C的预设副档名是.m

创建classes

@interface

   
  1. Fraction.h
  2. #import
  3. @interfaceFraction:NSObject{
  4. intnumerator;
  5. intdenominator;
  6. }
  7. -(void)print;
  8. -(void)setNumerator:(int)n;
  9. -(void)setDenominator:(int)d;
  10. -(int)numerator;
  11. -(int)denominator;
  12. @end

NSObject:NeXTStep Object 的缩写。因为它已经改名为 OpenStep,所以这在今天已经不是那么有意义了。

继承(inheritance)以 Class: Parent 表示,就像上面的 Fraction: NSObject。

夹在 @interface Class: Parent { .... } 中的称为 instance variables。

没有设定存取权限(protected, public, private)时,预设的存取权限为 protected。设定权限的方式将在稍后说明。

Instance methods 跟在成员变数(即 instance variables)后。格式为:scope (returnType) methodName: (parameter1Type) parameter1Name;

scope 有class 或 instance 两种。instance methods 以 - 开头,class level methods 以 + 开头。

Interface 以一个 @end 作为结束。

@implementation

   
  1. Fraction.m
  2. #import"Fraction.h"
  3. #import
  4. @implementationFraction
  5. -(void)print{
  6. printf("%i/%i",numerator,denominator);
  7. }
  8. -(void)setNumerator:(int)n{
  9. nnumerator=n;
  10. }
  11. -(void)setDenominator:(int)d{
  12. ddenominator=d;
  13. }
  14. -(int)denominator{
  15. returndenominator;
  16. }
  17. -(int)numerator{
  18. returnnumerator;
  19. }
  20. @end

Implementation 以 @implementation ClassName 开始,以 @end 结束。

Implement 定义好的 methods 的方式,跟在 interface 中宣告时很近似。

把它们凑在一起

   
  1. main.m
  2. #import
  3. #import"Fraction.h"
  4. intmain(intargc,constchar*argv[]){
  5. //createanewinstance
  6. Fraction*frac=[[Fractionalloc]init];
  7. //setthevalues
  8. [fracsetNumerator:1];
  9. [fracsetDenominator:3];
  10. //printit
  11. printf("Thefractionis:");
  12. [fracprint];
  13. printf("\n");
  14. //freememory
  15. [fracrelease];
  16. return0;
  17. }

output

   
  1. Thefractionis:1/3
  2. Fraction*frac=[[Fractionalloc]init];

这行程式码中有很多重要的东西。

在 Objective-C 中唿叫 methods 的方法是 [object method],就像 C++ 的 object->method()。

Objective-C 没有 value 型别。所以没有像 C++ 的 Fraction frac; frac.print(); 这类的东西。在 Objective-C 中完全使用指标来处理物件。

这行程式码实际上做了两件事: [Fraction alloc] 唿叫了 Fraction class 的 alloc method。这就像 malloc 记忆体,这个动作也做了一样的事情。

[object init] 是一个建构子(constructor)唿叫,负责初始化物件中的所有变数。它唿叫了 [Fraction alloc] 传回的 instance 上的 init method。这个动作非常普遍,所以通常以一行程式完成:Object *var = [[Object alloc] init];

[frac setNumerator: 1] 非常简单。它唿叫了 frac 上的 setNumerator method 并传入 1 为参数。

如同每个 C 的变体,Objective-C 也有一个用以释放记忆体的方式: release。它继承自 NSObject,这个 method 在之后会有详尽的解说。

详细说明...

多重参数

目前为止我还没展示如何传递多个参数。这个语法乍看之下不是很直觉,不过它却是来自一个十分受欢迎的 Smalltalk 版本。

   
  1. Fraction.h
  2. ...
  3. -(void)setNumerator:(int)nandDenominator:(int)d;
  4. ...
  5. Fraction.m
  6. ...
  7. -(void)setNumerator:(int)nandDenominator:(int)d{
  8. nnumerator=n;
  9. ddenominator=d;
  10. }
  11. ...
  12. main.m
  13. #import
  14. #import"Fraction.h"
  15. intmain(intargc,constchar*argv[]){
  16. //createanewinstance
  17. Fraction*frac=[[Fractionalloc]init];
  18. Fraction*frac2=[[Fractionalloc]init];
  19. //setthevalues
  20. [fracsetNumerator:1];
  21. [fracsetDenominator:3];
  22. //combinedset
  23. [frac2setNumerator:1andDenominator:5];
  24. //printit
  25. printf("Thefractionis:");
  26. [fracprint];
  27. printf("\n");
  28. //printit
  29. printf("Fraction2is:");
  30. [frac2print];
  31. printf("\n");
  32. //freememory
  33. [fracrelease];
  34. [frac2release];
  35. return0;
  36. }

output

   
  1. Thefractionis:1/3
  2. Fraction2is:1/5

这个 method 实际上叫做 setNumerator:andDenominator:

加入其他参数的方法就跟加入第二个时一样,即 method:label1:label2:label3: ,而唿叫的方法是 [obj method: param1 label1: param2 label2: param3 label3: param4]

Labels 是非必要的,所以可以有一个像这样的 method:method:::,简单的省略 label 名称,但以 : 区隔参数。并不建议这样使用。

建构子(Constructors)

   
  1. Fraction.h
  2. ...
  3. -(Fraction*)initWithNumerator:(int)ndenominator:(int)d;
  4. ...
  5. Fraction.m
  6. ...
  7. -(Fraction*)initWithNumerator:(int)ndenominator:(int)d{
  8. self=[superinit];
  9. if(self){
  10. [selfsetNumerator:nandDenominator:d];
  11. }
  12. returnself;
  13. }
  14. ...
  15. main.m
  16. #import
  17. #import"Fraction.h"
  18. intmain(intargc,constchar*argv[]){
  19. //createanewinstance
  20. Fraction*frac=[[Fractionalloc]init];
  21. Fraction*frac2=[[Fractionalloc]init];
  22. Fraction*frac3=[[Fractionalloc]initWithNumerator:3denominator:10];
  23. //setthevalues
  24. [fracsetNumerator:1];
  25. [fracsetDenominator:3];
  26. //combinedset
  27. [frac2setNumerator:1andDenominator:5];
  28. //printit
  29. printf("Thefractionis:");
  30. [fracprint];
  31. printf("\n");
  32. printf("Fraction2is:");
  33. [frac2print];
  34. printf("\n");
  35. printf("Fraction3is:");
  36. [frac3print];
  37. printf("\n");
  38. //freememory
  39. [fracrelease];
  40. [frac2release];
  41. [frac3release];
  42. return0;
  43. }

output

   
  1. Thefractionis:1/3
  2. Fraction2is:1/5
  3. Fraction3is:3/10

@interface 裡的宣告就如同正常的函式。

@implementation 使用了一个新的关键字:super

如同 Java,Objective-C 只有一个 parent class(父类别)。

使用 [super init] 来存取 Super constructor,这个动作需要适当的继承设计。

你将这个动作回传的 instance 指派给另一新个关键字:self。Self 很像 C++ 与 Java 的 this 指标。

if ( self ) 跟 ( self != nil ) 一样,是为了确定 super constructor 成功传回了一个新物件。nil 是 Objective-C 用来表达 C/C++ 中 NULL 的方式,可以引入 NSObject 来取得。

当你初始化变数以后,你用传回 self 的方式来传回自己的位址。

预设的建构子是 -(id) init。

技术上来说,Objective-C 中的建构子就是一个 "init" method,而不像 C++ 与 Java 有特殊的结构。

存取权限

预设的权限是 @protected

Java 实作的方式是在 methods 与变数前面加上 public/private/protected 修饰语,而 Objective-C 的作法则更像 C++ 对于 instance variable(译注:C++ 术语一般称为 data members)的方式。

   
  1. Access.h
  2. #import
  3. @interfaceAccess:NSObject{
  4. @public
  5. intpublicVar;
  6. @private
  7. intprivateVar;
  8. intprivateVar2;
  9. @protected
  10. intprotectedVar;
  11. }
  12. @end
  13. Access.m
  14. #import"Access.h"
  15. @implementationAccess
  16. @end
  17. main.m
  18. #import"Access.h"
  19. #import
  20. intmain(intargc,constchar*argv[]){
  21. Access*a=[[Accessalloc]init];
  22. //works
  23. a->publicVar=5;
  24. printf("publicvar:%i\n",a->publicVar);
  25. //doesn'tcompile
  26. //a->privateVar=10;
  27. //printf("privatevar:%i\n",a->privateVar);
  28. [arelease];
  29. return0;
  30. }

output

   
  1. publicvar:5

如同你所看到的,就像 C++ 中 private: [list of vars] public: [list of vars] 的格式,它只是改成了@private, @protected, 等等。

Class level access

   
  1. ClassA.h
  2. #import
  3. staticintcount;
  4. @interfaceClassA:NSObject
  5. +(int)initCount;
  6. +(void)initialize;
  7. @end
  8. ClassA.m
  9. #import"ClassA.h"
  10. @implementationClassA
  11. -(id)init{
  12. self=[superinit];
  13. count++;
  14. returnself;
  15. }
  16. +(int)initCount{
  17. returncount;
  18. }
  19. +(void)initialize{
  20. count=0;
  21. }
  22. @end
  23. main.m
  24. #import"ClassA.h"
  25. #import
  26. intmain(intargc,constchar*argv[]){
  27. ClassA*c1=[[ClassAalloc]init];
  28. ClassA*c2=[[ClassAalloc]init];
  29. //printcount
  30. printf("ClassAcount:%i\n",[ClassAinitCount]);
  31. ClassA*c3=[[ClassAalloc]init];
  32. //printcountagain
  33. printf("ClassAcount:%i\n",[ClassAinitCount]);
  34. [c1release];
  35. [c2release];
  36. [c3release];
  37. return0;
  38. }

output

   
  1. ClassAcount:2
  2. ClassAcount:3

static int count = 0; 这是 class variable 宣告的方式。其实这种变数摆在这裡并不理想,比较好的解法是像 Java 实作 static class variables 的方法。然而,它确实能用。

+(int) initCount; 这是回传 count 值的实际 method。请注意这细微的差别!这裡在 type 前面不用减号 - 而改用加号 +。加号 + 表示这是一个 class level function。(译注:许多文件中,class level functions 被称为 class functions 或 class method)

存取这个变数跟存取一般成员变数没有两样,就像 ClassA 中的 count++ 用法。

+(void) initialize method is 在 Objective-C 开始执行你的程式时被唿叫,而且它也被每个 class 唿叫。这是初始化像我们的 count 这类 class level variables 的好地方。

异常情况(Exceptions)

注意:异常处理只有 Mac OS X 10.3 以上才支援。

   
  1. CupWarningException.h
  2. #import
  3. @interfaceCupWarningException:NSException
  4. @end
  5. CupWarningException.m
  6. #import"CupWarningException.h"
  7. @implementationCupWarningException
  8. @end
  9. CupOverflowException.h
  10. #import
  11. @interfaceCupOverflowException:NSException
  12. @end
  13. CupOverflowException.m
  14. #import"CupOverflowException.h"
  15. @implementationCupOverflowException
  16. @end
  17. Cup.h
  18. #import
  19. @interfaceCup:NSObject{
  20. intlevel;
  21. }
  22. -(int)level;
  23. -(void)setLevel:(int)l;
  24. -(void)fill;
  25. -(void)empty;
  26. -(void)print;
  27. @end
  28. Cup.m
  29. #import"Cup.h"
  30. #import"CupOverflowException.h"
  31. #import"CupWarningException.h"
  32. #import
  33. #import
  34. @implementationCup
  35. -(id)init{
  36. self=[superinit];
  37. if(self){
  38. [selfsetLevel:0];
  39. }
  40. returnself;
  41. }
  42. -(int)level{
  43. returnlevel;
  44. }
  45. -(void)setLevel:(int)l{
  46. llevel=l;
  47. if(level>100){
  48. //throwoverflow
  49. NSException*e=[CupOverflowException
  50. exceptionWithName:@"CupOverflowException"
  51. reason:@"Thelevelisabove100"
  52. userInfo:nil];
  53. @throwe;
  54. }elseif(level>=50){
  55. //throwwarning
  56. NSException*e=[CupWarningException
  57. exceptionWithName:@"CupWarningException"
  58. reason:@"Thelevelisaboveorat50"
  59. userInfo:nil];
  60. @throwe;
  61. }elseif(level<0){
  62. //throwexception
  63. NSException*e=[NSException
  64. exceptionWithName:@"CupUnderflowException"
  65. reason:@"Thelevelisbelow0"
  66. userInfo:nil];
  67. @throwe;
  68. }
  69. }
  70. -(void)fill{
  71. [selfsetLevel:level+10];
  72. }
  73. -(void)empty{
  74. [selfsetLevel:level-10];
  75. }
  76. -(void)print{
  77. printf("Cuplevelis:%i\n",level);
  78. }
  79. @end
  80. main.m
  81. #import"Cup.h"
  82. #import"CupOverflowException.h"
  83. #import"CupWarningException.h"
  84. #import
  85. #import
  86. #import
  87. #import
  88. intmain(intargc,constchar*argv[]){
  89. NSAutoreleasePool*pool=[[NSAutoreleasePoolalloc]init];
  90. Cup*cup=[[Cupalloc]init];
  91. inti;
  92. //thiswillwork
  93. for(i=0;i<4;i++){
  94. [cupfill];
  95. [cupprint];
  96. }
  97. //thiswillthrowexceptions
  98. for(i=0;i<7;i++){
  99. @try{
  100. [cupfill];
  101. }@catch(CupWarningException*e){
  102. printf("%s:",[[ename]cString]);
  103. }@catch(CupOverflowException*e){
  104. printf("%s:",[[ename]cString]);
  105. }@finally{
  106. [cupprint];
  107. }
  108. }
  109. //throwagenericexception
  110. @try{
  111. [cupsetLevel:-1];
  112. }@catch(NSException*e){
  113. printf("%s:%s\n",[[ename]cString],[[ereason]cString]);
  114. }
  115. //freememory
  116. [cuprelease];
  117. [poolrelease];
  118. }

output

   
  1. Cuplevelis:10
  2. Cuplevelis:20
  3. Cuplevelis:30
  4. Cuplevelis:40
  5. CupWarningException:Cuplevelis:50
  6. CupWarningException:Cuplevelis:60
  7. CupWarningException:Cuplevelis:70
  8. CupWarningException:Cuplevelis:80
  9. CupWarningException:Cuplevelis:90
  10. CupWarningException:Cuplevelis:100
  11. CupOverflowException:Cuplevelis:110
  12. CupUnderflowException:Thelevelisbelow0

NSAutoreleasePool 是一个记忆体管理类别。现在先别管它是干嘛的。

Exceptions(异常情况)的丢出不需要扩充(extend)NSException 物件,你可简单的用 id 来代表它: @catch ( id e ) { ... }

还有一个 finally 区块,它的行为就像 Java 的异常处理方式,finally 区块的内容保证会被唿叫。

Cup.m 裡的 @"CupOverflowException" 是一个 NSString 常数物件。在 Objective-C 中,@ 符号通常用来代表这是语言的衍生部分。C 语言形式的字串(C string)就像 C/C++ 一样是 "String constant" 的形式,型别为 char *。


你可能感兴趣的:(Objective-C)