Objective-C学习大纲 二



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

继承、多型(Inheritance, Polymorphism)以及其他物件导向功能

id 型别

Objective-C 有种叫做 id 的型别,它的运作有时候像是 void*,不过它却严格规定只能用在物件。Objective-C 与 Java 跟 C++ 不一样,你在唿叫一个物件的 method 时,并不需要知道这个物件的型别。当然这个 method 一定要存在,这称为 Objective-C 的讯息传递。

 
  
  1. Fraction.h
  2. #import
  3. @interfaceFraction:NSObject{
  4. intnumerator;
  5. intdenominator;
  6. }
  7. -(Fraction*)initWithNumerator:(int)ndenominator:(int)d;
  8. -(void)print;
  9. -(void)setNumerator:(int)d;
  10. -(void)setDenominator:(int)d;
  11. -(void)setNumerator:(int)nandDenominator:(int)d;
  12. -(int)numerator;
  13. -(int)denominator;
  14. @end
  15. Fraction.m
  16. #import"Fraction.h"
  17. #import
  18. @implementationFraction
  19. -(Fraction*)initWithNumerator:(int)ndenominator:(int)d{
  20. self=[superinit];
  21. if(self){
  22. [selfsetNumerator:nandDenominator:d];
  23. }
  24. returnself;
  25. }
  26. -(void)print{
  27. printf("%i/%i",numerator,denominator);
  28. }
  29. -(void)setNumerator:(int)n{
  30. nnumerator=n;
  31. }
  32. -(void)setDenominator:(int)d{
  33. ddenominator=d;
  34. }
  35. -(void)setNumerator:(int)nandDenominator:(int)d{
  36. nnumerator=n;
  37. ddenominator=d;
  38. }
  39. -(int)denominator{
  40. returndenominator;
  41. }
  42. -(int)numerator{
  43. returnnumerator;
  44. }
  45. @end
  46. Complex.h
  47. #import
  48. @interfaceComplex:NSObject{
  49. doublereal;
  50. doubleimaginary;
  51. }
  52. -(Complex*)initWithReal:(double)randImaginary:(double)i;
  53. -(void)setReal:(double)r;
  54. -(void)setImaginary:(double)i;
  55. -(void)setReal:(double)randImaginary:(double)i;
  56. -(double)real;
  57. -(double)imaginary;
  58. -(void)print;
  59. @end
  60. Complex.m
  61. #import"Complex.h"
  62. #import
  63. @implementationComplex
  64. -(Complex*)initWithReal:(double)randImaginary:(double)i{
  65. self=[superinit];
  66. if(self){
  67. [selfsetReal:randImaginary:i];
  68. }
  69. returnself;
  70. }
  71. -(void)setReal:(double)r{
  72. rreal=r;
  73. }
  74. -(void)setImaginary:(double)i{
  75. iimaginary=i;
  76. }
  77. -(void)setReal:(double)randImaginary:(double)i{
  78. rreal=r;
  79. iimaginary=i;
  80. }
  81. -(double)real{
  82. returnreal;
  83. }
  84. -(double)imaginary{
  85. returnimaginary;
  86. }
  87. -(void)print{
  88. printf("%_f+%_fi",real,imaginary);
  89. }
  90. @end
  91. main.m
  92. #import
  93. #import"Fraction.h"
  94. #import"Complex.h"
  95. intmain(intargc,constchar*argv[]){
  96. //createanewinstance
  97. Fraction*frac=[[Fractionalloc]initWithNumerator:1denominator:10];
  98. Complex*comp=[[Complexalloc]initWithReal:10andImaginary:15];
  99. idnumber;
  100. //printfraction
  101. number=frac;
  102. printf("Thefractionis:");
  103. [numberprint];
  104. printf("\n");
  105. //printcomplex
  106. number=comp;
  107. printf("Thecomplexnumberis:");
  108. [numberprint];
  109. printf("\n");
  110. //freememory
  111. [fracrelease];
  112. [comprelease];
  113. return0;
  114. }

output

 
  
  1. Thefractionis:1/10
  2. Thecomplexnumberis:10.000000+15.000000i

这种动态连结有显而易见的好处。你不需要知道你唿叫 method 的那个东西是什么型别,如果这个物件对这个讯息有反应,那就会唤起这个 method。这也不会牵涉到一堆繁琐的转型动作,比如在 Java 裡唿叫一个整数物件的 .intValue() 就得先转型,然后才能唿叫这个 method。

继承(Inheritance)

 
  
  1. Rectangle.h
  2. #import
  3. @interfaceRectangle:NSObject{
  4. intwidth;
  5. intheight;
  6. }
  7. -(Rectangle*)initWithWidth:(int)wheight:(int)h;
  8. -(void)setWidth:(int)w;
  9. -(void)setHeight:(int)h;
  10. -(void)setWidth:(int)wheight:(int)h;
  11. -(int)width;
  12. -(int)height;
  13. -(void)print;
  14. @end
  15. Rectangle.m
  16. #import"Rectangle.h"
  17. #import
  18. @implementationRectangle
  19. -(Rectangle*)initWithWidth:(int)wheight:(int)h{
  20. self=[superinit];
  21. if(self){
  22. [selfsetWidth:wheight:h];
  23. }
  24. returnself;
  25. }
  26. -(void)setWidth:(int)w{
  27. wwidth=w;
  28. }
  29. -(void)setHeight:(int)h{
  30. hheight=h;
  31. }
  32. -(void)setWidth:(int)wheight:(int)h{
  33. wwidth=w;
  34. hheight=h;
  35. }
  36. -(int)width{
  37. returnwidth;
  38. }
  39. -(int)height{
  40. returnheight;
  41. }
  42. -(void)print{
  43. printf("width=%i,height=%i",width,height);
  44. }
  45. @end
  46. Square.h
  47. #import"Rectangle.h"
  48. @interfaceSquare:Rectangle
  49. -(Square*)initWithSize:(int)s;
  50. -(void)setSize:(int)s;
  51. -(int)size;
  52. @end
  53. Square.m
  54. #import"Square.h"
  55. @implementationSquare
  56. -(Square*)initWithSize:(int)s{
  57. self=[superinit];
  58. if(self){
  59. [selfsetSize:s];
  60. }
  61. returnself;
  62. }
  63. -(void)setSize:(int)s{
  64. width=s;
  65. height=s;
  66. }
  67. -(int)size{
  68. returnwidth;
  69. }
  70. -(void)setWidth:(int)w{
  71. [selfsetSize:w];
  72. }
  73. -(void)setHeight:(int)h{
  74. [selfsetSize:h];
  75. }
  76. @end
  77. main.m
  78. #import"Square.h"
  79. #import"Rectangle.h"
  80. #import
  81. intmain(intargc,constchar*argv[]){
  82. Rectangle*rec=[[Rectanglealloc]initWithWidth:10height:20];
  83. Square*sq=[[Squarealloc]initWithSize:15];
  84. //printem
  85. printf("Rectangle:");
  86. [recprint];
  87. printf("\n");
  88. printf("Square:");
  89. [sqprint];
  90. printf("\n");
  91. //updatesquare
  92. [sqsetWidth:20];
  93. printf("Squareafterchange:");
  94. [sqprint];
  95. printf("\n");
  96. //freememory
  97. [recrelease];
  98. [sqrelease];
  99. return0;
  100. }

output

 
  
  1. Rectangle:width=10,height=20
  2. Square:width=15,height=15
  3. Squareafterchange:width=20,height=20

继承在 Objective-C 裡比较像 Java。当你扩充你的 super class(所以只能有一个 parent),你想自订这个 super class 的 method,只要简单的在你的 child class implementation 裡放上新的实作内容即可。而不需要 C++ 裡呆呆的 virtual table。

这裡还有一个值得玩味的地方,如果你企图像这样去唿叫 rectangle 的 constructor: Square *sq = [[Square alloc] initWithWidth: 10 height: 15],会发生什么事?答案是会产生一个编译器错误。因为 rectangle constructor 回传的型别是 Rectangle*,而不是 Square*,所以这行不通。在某种情况下如果你真想这样用,使用 id 型别会是很好的选择。如果你想使用 parent 的 constructor,只要把 Rectangle* 回传型别改成 id 即可。

动态识别(Dynamic types)

这裡有一些用于 Objective-C 动态识别的 methods(说明部分採中英并列,因为我觉得英文比较传神,中文怎么译都怪):

 
  
  1. -(BOOL)isKindOfClass:classObjisobjectadescendentormemberofclassObj

此物件是否是 classObj 的子孙或一员

 
  
  1. -(BOOL)isMemberOfClass:classObjisobjectamemberofclassObj

此物件是否是 classObj 的一员

 
  
  1. -(BOOL)respondsToSelector:selectordoestheobjecthaveamethodnamedspecifiecbytheselector

此物件是否有叫做 selector 的 method

 
  
  1. +(BOOL)instancesRespondToSelector:selectordoesanobjectcreatedbythisclasshavetheabilitytorespondtothespecifiedselector

此物件是否是由有能力回应指定 selector 的物件所产生

 
  
  1. -(id)performSelector:selectorinvokethespecifiedselectorontheobject

唤起此物件的指定 selector

所有继承自 NSObject 都有一个可回传一个 class 物件的 class method。这非常近似于 Java 的 getClass() method。这个 class 物件被使用于前述的 methods 中。

Selectors 在 Objective-C 用以表示讯息。下一个範例会秀出建立 selector 的语法。

 
  
  1. main.m
  2. #import"Square.h"
  3. #import"Rectangle.h"
  4. #import
  5. intmain(intargc,constchar*argv[]){
  6. Rectangle*rec=[[Rectanglealloc]initWithWidth:10height:20];
  7. Square*sq=[[Squarealloc]initWithSize:15];
  8. //isMemberOfClass
  9. //true
  10. if([sqisMemberOfClass:[Squareclass]]==YES){
  11. printf("squareisamemberofsquareclass\n");
  12. }
  13. //false
  14. if([sqisMemberOfClass:[Rectangleclass]]==YES){
  15. printf("squareisamemberofrectangleclass\n");
  16. }
  17. //false
  18. if([sqisMemberOfClass:[NSObjectclass]]==YES){
  19. printf("squareisamemberofobjectclass\n");
  20. }
  21. //isKindOfClass
  22. //true
  23. if([sqisKindOfClass:[Squareclass]]==YES){
  24. printf("squareisakindofsquareclass\n");
  25. }
  26. //true
  27. if([sqisKindOfClass:[Rectangleclass]]==YES){
  28. printf("squareisakindofrectangleclass\n");
  29. }
  30. //true
  31. if([sqisKindOfClass:[NSObjectclass]]==YES){
  32. printf("squareisakindofobjectclass\n");
  33. }
  34. //respondsToSelector
  35. //true
  36. if([sqrespondsToSelector:@selector(setSize:)]==YES){
  37. printf("squarerespondstosetSize:method\n");
  38. }
  39. //false
  40. if([sqrespondsToSelector:@selector(nonExistant)]==YES){
  41. printf("squarerespondstononExistantmethod\n");
  42. }
  43. //true
  44. if([SquarerespondsToSelector:@selector(alloc)]==YES){
  45. printf("squareclassrespondstoallocmethod\n");
  46. }
  47. //instancesRespondToSelector
  48. //false
  49. if([RectangleinstancesRespondToSelector:@selector(setSize:)]==YES){
  50. printf("rectangleinstancerespondstosetSize:method\n");
  51. }
  52. //true
  53. if([SquareinstancesRespondToSelector:@selector(setSize:)]==YES){
  54. printf("squareinstancerespondstosetSize:method\n");
  55. }
  56. //freememory
  57. [recrelease];
  58. [sqrelease];
  59. return0;
  60. }

output

 
  
  1. squareisamemberofsquareclass
  2. squareisakindofsquareclass
  3. squareisakindofrectangleclass
  4. squareisakindofobjectclass
  5. squarerespondstosetSize:method
  6. squareclassrespondstoallocmethod
  7. squareinstancerespondstosetSize:method
  8. Categories

当你想要为某个 class 新增 methods,你通常会扩充(extend,即继承)它。然而这不一定是个完美解法,特别是你想要重写一个 class 的某个功能,但你却没有塬始码时。Categories 允许你在现有的 class 加入新功能,但不需要扩充它。Ruby 语言也有类似的功能。

 
  
  1. FractionMath.h
  2. #import"Fraction.h"
  3. @interfaceFraction(Math)
  4. -(Fraction*)add:(Fraction*)f;
  5. -(Fraction*)mul:(Fraction*)f;
  6. -(Fraction*)div:(Fraction*)f;
  7. -(Fraction*)sub:(Fraction*)f;
  8. @end
  9. FractionMath.m
  10. #import"FractionMath.h"
  11. @implementationFraction(Math)
  12. -(Fraction*)add:(Fraction*)f{
  13. return[[Fractionalloc]initWithNumerator:numerator*[fdenominator]+
  14. denominator*[fnumerator]
  15. denominator:denominator*[fdenominator]];
  16. }
  17. -(Fraction*)mul:(Fraction*)f{
  18. return[[Fractionalloc]initWithNumerator:numerator*[fnumerator]
  19. denominator:denominator*[fdenominator]];
  20. }
  21. -(Fraction*)div:(Fraction*)f{
  22. return[[Fractionalloc]initWithNumerator:numerator*[fdenominator]
  23. denominator:denominator*[fnumerator]];
  24. }
  25. -(Fraction*)sub:(Fraction*)f{
  26. return[[Fractionalloc]initWithNumerator:numerator*[fdenominator]-
  27. denominator*[fnumerator]
  28. denominator:denominator*[fdenominator]];
  29. }
  30. @end
  31. main.m
  32. #import
  33. #import"Fraction.h"
  34. #import"FractionMath.h"
  35. intmain(intargc,constchar*argv[]){
  36. //createanewinstance
  37. Fraction*frac1=[[Fractionalloc]initWithNumerator:1denominator:3];
  38. Fraction*frac2=[[Fractionalloc]initWithNumerator:2denominator:5];
  39. Fraction*frac3=[frac1mul:frac2];
  40. //printit
  41. [frac1print];
  42. printf("*");
  43. [frac2print];
  44. printf("=");
  45. [frac3print];
  46. printf("\n");
  47. //freememory
  48. [frac1release];
  49. [frac2release];
  50. [frac3release];
  51. return0;
  52. }

output

 
  
  1. 1/3*2/5=2/15

重点是 @implementation 跟 @interface 这两行:@interface Fraction (Math) 以及 @implementation Fraction (Math).

(同一个 class)只能有一个同名的 category,其他的 categories 得加上不同的、独一无二的名字。

Categories 在建立 private methods 时十分有用。因为 Objective-C 并没有像 Java 这种 private/protected/public methods 的概念,所以必须要使用 categories 来达成这种功能。作法是把 private method 从你的 class header (.h) 档案移到 implementation (.m) 档案。以下是此种作法一个简短的範例。

 
  
  1. MyClass.h
  2. #import
  3. @interfaceMyClass:NSObject
  4. -(void)publicMethod;
  5. @end
  6. MyClass.m
  7. #import"MyClass.h"
  8. #import
  9. @implementationMyClass
  10. -(void)publicMethod{
  11. printf("publicmethod\n");
  12. }
  13. @end
  14. //privatemethods
  15. @interfaceMyClass(Private)
  16. -(void)privateMethod;
  17. @end
  18. @implementationMyClass(Private)
  19. -(void)privateMethod{
  20. printf("privatemethod\n");
  21. }
  22. @end
  23. main.m
  24. #import"MyClass.h"
  25. intmain(intargc,constchar*argv[]){
  26. MyClass*obj=[[MyClassalloc]init];
  27. //thiscompiles
  28. [objpublicMethod];
  29. //thisthrowserrorswhencompiling
  30. //[objprivateMethod];
  31. //freememory
  32. [objrelease];
  33. return0;
  34. }

output

 
  
  1. publicmethod
  2. Posing

Posing 有点像 categories,但是不太一样。它允许你扩充一个 class,并且全面性地的扮演(pose)这个 super class。例如:你有一个扩充 NSArray 的 NSArrayChild 物件。如果你让 NSArrayChild 扮演 NSArray,则在你的程式码中所有的 NSArray 都会自动被替代为 NSArrayChild。

 
  
  1. FractionB.h
  2. #import"Fraction.h"
  3. @interfaceFractionB:Fraction
  4. -(void)print;
  5. @end
  6. FractionB.m
  7. #import"FractionB.h"
  8. #import
  9. @implementationFractionB
  10. -(void)print{
  11. printf("(%i/%i)",numerator,denominator);
  12. }
  13. @end
  14. main.m
  15. #import
  16. #import"Fraction.h"
  17. #import"FractionB.h"
  18. intmain(intargc,constchar*argv[]){
  19. Fraction*frac=[[Fractionalloc]initWithNumerator:3denominator:10];
  20. //printit
  21. printf("Thefractionis:");
  22. [fracprint];
  23. printf("\n");
  24. //makeFractionBposeasFraction
  25. [FractionBposeAsClass:[Fractionclass]];
  26. Fraction*frac2=[[Fractionalloc]initWithNumerator:3denominator:10];
  27. //printit
  28. printf("Thefractionis:");
  29. [frac2print];
  30. printf("\n");
  31. //freememory
  32. [fracrelease];
  33. [frac2release];
  34. return0;
  35. }

output

 
  
  1. Thefractionis:3/10
  2. Thefractionis:(3/10)

这个程式的输出中,第一个 fraction 会输出 3/10,而第二个会输出 (3/10)。这是 FractionB 中实作的方式。

poseAsClass 这个 method 是 NSObject 的一部份,它允许 subclass 扮演 superclass。

Protocols

Objective-C 裡的 Protocol 与 Java 的 interface 或是 C++ 的 purely virtual class 相同。

 
  
  1. Printing.h
  2. @protocolPrinting
  3. -(void)print;
  4. @end
  5. Fraction.h
  6. #import
  7. #import"Printing.h"
  8. @interfaceFraction:NSObject{
  9. intnumerator;
  10. intdenominator;
  11. }
  12. -(Fraction*)initWithNumerator:(int)ndenominator:(int)d;
  13. -(void)setNumerator:(int)d;
  14. -(void)setDenominator:(int)d;
  15. -(void)setNumerator:(int)nandDenominator:(int)d;
  16. -(int)numerator;
  17. -(int)denominator;
  18. @end
  19. Fraction.m
  20. #import"Fraction.h"
  21. #import
  22. @implementationFraction
  23. -(Fraction*)initWithNumerator:(int)ndenominator:(int)d{
  24. self=[superinit];
  25. if(self){
  26. [selfsetNumerator:nandDenominator:d];
  27. }
  28. returnself;
  29. }
  30. -(void)print{
  31. printf("%i/%i",numerator,denominator);
  32. }
  33. -(void)setNumerator:(int)n{
  34. nnumerator=n;
  35. }
  36. -(void)setDenominator:(int)d{
  37. ddenominator=d;
  38. }
  39. -(void)setNumerator:(int)nandDenominator:(int)d{
  40. nnumerator=n;
  41. ddenominator=d;
  42. }
  43. -(int)denominator{
  44. returndenominator;
  45. }
  46. -(int)numerator{
  47. returnnumerator;
  48. }
  49. -(Fraction*)copyWithZone:(NSZone*)zone{
  50. return[[FractionallocWithZone:zone]initWithNumerator:numerator
  51. denominator:denominator];
  52. }
  53. @end
  54. Complex.h
  55. #import
  56. #import"Printing.h"
  57. @interfaceComplex:NSObject{
  58. doublereal;
  59. doubleimaginary;
  60. }
  61. -(Complex*)initWithReal:(double)randImaginary:(double)i;
  62. -(void)setReal:(double)r;
  63. -(void)setImaginary:(double)i;
  64. -(void)setReal:(double)randImaginary:(double)i;
  65. -(double)real;
  66. -(double)imaginary;
  67. @end
  68. Complex.m
  69. #import"Complex.h"
  70. #import
  71. @implementationComplex
  72. -(Complex*)initWithReal:(double)randImaginary:(double)i{
  73. self=[superinit];
  74. if(self){
  75. [selfsetReal:randImaginary:i];
  76. }
  77. returnself;
  78. }
  79. -(void)setReal:(double)r{
  80. rreal=r;
  81. }
  82. -(void)setImaginary:(double)i{
  83. iimaginary=i;
  84. }
  85. -(void)setReal:(double)randImaginary:(double)i{
  86. rreal=r;
  87. iimaginary=i;
  88. }
  89. -(double)real{
  90. returnreal;
  91. }
  92. -(double)imaginary{
  93. returnimaginary;
  94. }
  95. -(void)print{
  96. printf("%_f+%_fi",real,imaginary);
  97. }
  98. @end
  99. main.m
  100. #import
  101. #import"Fraction.h"
  102. #import"Complex.h"
  103. intmain(intargc,constchar*argv[]){
  104. //createanewinstance
  105. Fraction*frac=[[Fractionalloc]initWithNumerator:3denominator:10];
  106. Complex*comp=[[Complexalloc]initWithReal:5andImaginary:15];
  107. idprintable;
  108. idcopyPrintable;
  109. //printit
  110. printable=frac;
  111. printf("Thefractionis:");
  112. [printableprint];
  113. printf("\n");
  114. //printcomplex
  115. printable=comp;
  116. printf("Thecomplexnumberis:");
  117. [printableprint];
  118. printf("\n");
  119. //thiscompilesbecauseFractioncomformstobothPrintingandNSCopyable
  120. copyPrintable=frac;
  121. //thisdoesn'tcompilebecauseComplexonlyconformstoPrinting
  122. //copyPrintable=comp;
  123. //testconformance
  124. //true
  125. if([fracconformsToProtocol:@protocol(NSCopying)]==YES){
  126. printf("FractionconformstoNSCopying\n");
  127. }
  128. //false
  129. if([compconformsToProtocol:@protocol(NSCopying)]==YES){
  130. printf("ComplexconformstoNSCopying\n");
  131. }
  132. //freememory
  133. [fracrelease];
  134. [comprelease];
  135. return0;
  136. }

output

 
  
  1. Thefractionis:3/10
  2. Thecomplexnumberis:5.000000+15.000000i
  3. FractionconformstoNSCopying

protocol 的宣告十分简单,基本上就是 @protocol ProtocolName (methods you must implement) @end。

要遵从(conform)某个 protocol,将要遵从的 protocols 放在 <> 裡面,并以逗点分隔。如:@interface SomeClass

protocol 要求实作的 methods 不需要放在 header 档裡面的 methods 列表中。如你所见,Complex.h 档案裡没有 -(void) print 的宣告,却还是要实作它,因为它(Complex class)遵从了这个 protocol。

Objective-C 的介面系统有一个独一无二的观念是如何指定一个型别。比起 C++ 或 Java 的指定方式,如:Printing *someVar = ( Printing * ) frac; 你可以使用 id 型别加上 protocol:id var = frac;。这让你可以动态地指定一个要求多个 protocol 的型别,却从头到尾只用了一个变数。如: var = frac;

就像使用@selector 来测试物件的继承关係,你可以使用 @protocol 来测试物件是否遵从介面。如果物件遵从这个介面,[object conformsToProtocol: @protocol( SomeProtocol )] 会回传一个 YES 型态的 BOOL 物件。同样地,对 class 而言也能如法炮製 [SomeClass conformsToProtocol: @protocol( SomeProtocol )]。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值