
第一節(jié)總括
這一節(jié)是對(duì)Objective-C(以后簡(jiǎn)稱OC)的簡(jiǎn)要介紹,目的是使讀者對(duì)OC有一個(gè)概括的認(rèn)識(shí)。
1.面象的讀者
在閱讀本文之前,應(yīng)具備使用與C類似的編程語(yǔ)言(如C,C++,JAVA)的一些經(jīng)驗(yàn),同時(shí)熟悉面向?qū)ο缶幊獭?/p>
2.OC簡(jiǎn)介
OC是以SmallTalk為基礎(chǔ),建立在C語(yǔ)言之上,是C語(yǔ)言的超集。20世紀(jì)80年代早期由Brad J.Cox設(shè)計(jì),2007年蘋果公司發(fā)布了OC2.0,并在iPhone上使用OC進(jìn)行開發(fā)。
3.OC學(xué)習(xí)內(nèi)容
學(xué)習(xí)的內(nèi)容主要包括語(yǔ)法和Cocoa框架兩部分。本文主要對(duì)語(yǔ)法進(jìn)行介紹。
4.IDE
編寫OC程序最主要的編譯環(huán)境是Xcode,它是蘋果官方提供的IDE,官網(wǎng)中的SDK包括Xcode,可以通過(guò)下載SDK來(lái)獲得它。但是Xcode只支持MacOSX,所以如果要在其它環(huán)境下編寫OC程序,要使用其它IDE。Linux/FreeBSD用GNUStep,Windows NT5.x(2000,XP)要先安裝cywin或mingw,然后安裝GNUStep。同時(shí)僅僅通過(guò)文本編輯器,GCC的make工具也可以用于開發(fā)。
注:如果要使用到Cocoa的話,只能在Apple公司的Xcode上。
5.框架
OC編程中主要用到的框架是Cocoa,它是MacOSX中五大API之一,它由兩個(gè)不同的框架組成FoundationKit和ApplicationKit。Foundation框架擁有100多個(gè)類,其中有很多有用的、面向數(shù)據(jù)的低級(jí)類和數(shù)據(jù)類型,如NSString,NSArray,NSEnumerator和NSNumber。ApplicationKit包含了所有的用戶接口對(duì)象和高級(jí)類。這些框架本文不做重點(diǎn)介紹,如果要深入了解可以去看Xcode自帶的文檔。
6.特別之處
初次接觸OC時(shí),會(huì)發(fā)現(xiàn)許多和其它語(yǔ)言不同的地方,會(huì)看到很多的+,-,[ ,] ,@,NS等符號(hào),這些符號(hào)在以后的編程中將經(jīng)??吹?這部分內(nèi)容在第二節(jié)中介紹。先熟悉一下OC的代碼:
#import"ClassA.h"
#import
intmain( int argc, const char *argv[] ) {
ClassA*c1= [[ClassAalloc]init];
ClassA*c2= [[ClassAalloc]init];
//print count
printf("ClassAcount: %i\n", [ClassAinitCount] );
ClassA*c3= [[ClassAalloc]init];
//print count again
printf("ClassA count: %i\n", [ClassA initCount] );
[c1release];
[c2release];
[c3release];
return0;
}
除了這些語(yǔ)言要素上的不同,OC也提供了一些很好的特性,如類別,扮演(Posing)等,這些在運(yùn)行時(shí)的特性使得編程更加靈活。
7.優(yōu)缺點(diǎn)
每一個(gè)語(yǔ)言都有其優(yōu)缺點(diǎn),OC也不例外,這就要求在選擇語(yǔ)言時(shí)權(quán)衡利弊。對(duì)于OC,只要善于利用它的優(yōu)點(diǎn),你會(huì)發(fā)現(xiàn)它是一個(gè)簡(jiǎn)單,靈活,高效的語(yǔ)言。以下列舉了它的一些特點(diǎn):
優(yōu)點(diǎn):類別、扮演(Posing)、動(dòng)態(tài)類型、指針計(jì)算、彈性信息傳遞、不是一個(gè)過(guò)度復(fù)雜的c衍生語(yǔ)言、可通過(guò)Objective-c++與c++結(jié)合
缺點(diǎn):沒有命名空間、沒有操作符重載、不像c++那樣復(fù)雜
第二節(jié):對(duì)C的擴(kuò)展
1.擴(kuò)展名
OC是ANSI版本C的一個(gè)超集,它支持相同的C語(yǔ)言基本語(yǔ)法。與C一樣,文件分為頭文件和源文件,擴(kuò)展名分別為.h和.m。如果要加入c++的語(yǔ)法,需要用到.mm,這里不做介紹。
.h
頭文件。頭文件包涵類的定義、類型、方法以及常量的聲明
.m
源文件。這個(gè)典型的擴(kuò)展名用來(lái)定義源文件,可以同時(shí)包含C和Objective-C的代碼。
2.#import
在OC里,包含頭文件有比#include更好的方法#import。它的使用和#include相同,并且可以保證你的程序只包含相同的頭文件一次。相當(dāng)于#include+ #pragma once的組合。
例如要包含F(xiàn)oundation框架中的Foundation.h文件,可以像下面這樣。
#import
注:每個(gè)框架有一個(gè)主的頭文件,只要包含了這個(gè)文件,框架中的所有特性都可以被使用。
3.@符號(hào)
@符號(hào)是OC在C基礎(chǔ)上新加的特性之一。常見到的形式有@”字符串”,%@ , @interface,@implement等。@”字符串”表示引用的字符串應(yīng)該作為Cocoa的NSString元素來(lái)處理。@interface等則是對(duì)于C的擴(kuò)展,是OC面向?qū)ο筇匦缘捏w現(xiàn)。
注:這里提一個(gè)小技巧,只要看到@符號(hào),就可以認(rèn)為它是對(duì)于C的一個(gè)擴(kuò)展。
4.NSLog()
在OC中用的打印函數(shù)是NSLog(),因?yàn)镺C是加了一點(diǎn)”特殊語(yǔ)料”的C語(yǔ)言,所以也可以用printf()但是NSLog()提供了一些特性,如時(shí)間戳,日期戳和自動(dòng)加換行符等,用起來(lái)更方便,所以推薦使用NSLog()。下面是兩種輸出的對(duì)比。
使用NSLog()輸出任意對(duì)象的值時(shí),都會(huì)使用%@格式說(shuō)明。在使用這個(gè)說(shuō)明符時(shí),對(duì)象通過(guò)一個(gè)名為description的方法提供自己的NSLog()格式。
下面分別是使用NSLog()和使用printf()的相應(yīng)輸出:
2010-10-15 14:54:21。42610_15[1973:207] Hello World!
HelloWorld!
注:NS前綴告訴你函數(shù)來(lái)自Cocoa而不是其他工具包。
5.BOOL
BOOL是OC中的布爾類型,它和C中的bool有如下區(qū)別
BOOL
YES(1),NO(0)
bool
true(!0),false(0)
6.id
這是OC新加的一個(gè)數(shù)據(jù)類型,它是一般的對(duì)象類型,能夠存儲(chǔ)任何類型的方法。
7.nil
在OC中,相對(duì)于C中的NULL,用的是nil。這兩者是等價(jià)的。下面是nil的定義。
#definenil NULL
第三節(jié):創(chuàng)建對(duì)象
1.接口和實(shí)現(xiàn)
在OC中定義一個(gè)類需要有兩個(gè)部分:接口和實(shí)現(xiàn)。接口文件包含了類的聲明,定義了實(shí)例變量和方法。實(shí)現(xiàn)文件包含了具體的函數(shù)的實(shí)現(xiàn)代碼。下圖顯示了一個(gè)叫 MyClass的類,它繼承自NSObject基類。類的定義總是從@interface開始到@end結(jié)束。在類名后面的是父類的名稱。實(shí)例變量被定義 在兩個(gè)花括號(hào)之間。在實(shí)例變量下面的是方法的定義。一個(gè)分號(hào)用來(lái)結(jié)束一個(gè)變量或者方法。
下面的代碼顯示了MyClass這個(gè)類的實(shí)現(xiàn)代碼。就像類的定義規(guī)則一樣,類實(shí)現(xiàn)文件也被兩個(gè)標(biāo)識(shí)框起來(lái),一個(gè)是 @implementation,還有一個(gè)是@end。這兩個(gè)指令標(biāo)識(shí)符告訴編譯器程序從哪里開始編譯到哪里結(jié)束。類中的方法名稱的定義和它接口文件中的 定義是一樣的,除了實(shí)現(xiàn)文件中有具體的代碼以外。
@implementationMyClass
-(id)initWithString:(NSString *) aName
{
if(self = [super init]) {
countcount = 0;
data= nil;
name= [aName copy];
returnself;
}
}
+(MyClass *)createMyClassWithString: (NSString *) aName
{
return[[[self alloc] initWithString:aName] autorelease];
}
@end
當(dāng)你要把一個(gè)對(duì)象保存進(jìn)變量,要使用指針類型。OC同時(shí)支持強(qiáng)和弱變量對(duì)象。強(qiáng)類型對(duì)象在變量類型定義的時(shí)候包含了類名。弱對(duì)象使用id類型作為實(shí)例變量。下面的例子同時(shí)顯示了定義MyClass中的強(qiáng)弱兩種類型的變量
MyClass*myObject1;// Strong typing
idmyObject2;// Weak typing
2.方法
一個(gè)方法定義包含了方法類型,返回類型,一個(gè)或者多個(gè)關(guān)鍵詞,參數(shù)類型和參數(shù)名。在OC中一個(gè)類中的方法有兩種類型:實(shí)例方法,類方法。實(shí)例方法前用(-)號(hào)表明,類方法用(+)表明,通過(guò)下圖可以看到,前面有一個(gè)(-)號(hào),說(shuō)明這是一個(gè)實(shí)例方法。
在OC中,調(diào)用一個(gè)方法相當(dāng)于傳遞一個(gè)消息,這里的消息指的是方法名和參數(shù)。所有的消息的分派都是動(dòng)態(tài)的,這個(gè)體現(xiàn)了OC的多態(tài)性。消息調(diào)用的方式是使用方括號(hào)。如下面的例子中,向myArray對(duì)象發(fā)送insertObject:atIndex:這個(gè)消息。
[myArrayinsertObject:anObj atIndex:0];
這種消息傳遞允許嵌套
[[myAppObjectgetArray] insertObject:[myAppObject getObjectToInsert] atIndex:0];
前面的例子都是把消息傳遞給實(shí)例變量,你也可以把消息傳遞給類本身。這時(shí)要用類方法來(lái)替代實(shí)例方法 。你可以 把他想象成靜態(tài)C++類(當(dāng)然不完全相同)。
類方法的定義只有一個(gè)不一樣那就是用加號(hào)(+)代替減號(hào)(-)。下面就是使用一個(gè)類方法。
NSMutableArray*myArray = nil;// nil is essentially the same as NULL
//Create a new array and assign it to the myArray variable.
myArray =[NSMutableArray arrayWithCapacity:0];
3.屬性
屬性提供了比方法更方便的訪問(wèn)方式。通過(guò)property標(biāo)識(shí)符來(lái)替代getter和setter方法。使用方法就是在類接口文件中用@property標(biāo)識(shí)符,后面跟著變量的屬性,包括 copy, tetain, assign ,readonly , readwrite,nonatomic,然后是變量名。同時(shí)在實(shí)現(xiàn)文件中用@synthesize標(biāo)識(shí)符來(lái)取代getter和setter方法。
@propertyBOOL flag;
@property(copy) NSString* nameObject;
//Copy the object during assignment.
@property(readonly) UIView* rootView;// Create only a getter method
接口文件中使用@property
@synthesizeflag,nameObject,rootView;
實(shí)現(xiàn)文件中使用@synthesize
屬性的另一個(gè)好處就是,可以使用點(diǎn)(.)語(yǔ)法來(lái)訪問(wèn),如下所示:
myObject.flag= YES;
CGRectviewFrame = myObject.rootView.frame;
第四節(jié):繼承
繼承的語(yǔ)法如下,冒號(hào)后的標(biāo)識(shí)符是需要繼承的類。
@interfaceCircle : NSObject
1.不支持多繼承
要注意的是OC只支持單繼承,如果要實(shí)現(xiàn)多繼承的話,可以通過(guò)類別和協(xié)議的方式來(lái)實(shí)現(xiàn),這兩種方法將在后面進(jìn)行介紹。
2.Super關(guān)鍵字
OC提供某種方式來(lái)重寫方法,并且仍然調(diào)用超類的實(shí)現(xiàn)方式。當(dāng)需要超類實(shí)現(xiàn)自身的功能,同時(shí)在前面或后面執(zhí)行某些額外的工作時(shí),這種機(jī)制非常有用。為了調(diào)用繼承方法的實(shí)現(xiàn),需要使用super作為方法調(diào)用的目標(biāo)。下面是代碼示例:
@implementationCircle
-(void)setFillColor:(ShapeColor) c
{
if(c== kRedColor){
c= kGreenColor;
}
[supersetFillColor: c];
}
@end
Super來(lái)自哪里呢?它既不是參數(shù)也不是實(shí)例變量,而是由OC編譯器提供的某種神奇功能。向super發(fā)送消息時(shí),實(shí)際上是在請(qǐng)求OC向該類的超類發(fā)送消息。如果超類中沒在定義該消息,OC將按照通常的方式在繼承鏈中繼續(xù)查找對(duì)應(yīng)的消息。
第五節(jié):對(duì)象初始化
1.分配與初始化
對(duì)象的初始化有兩種方法:一種是[類名new], 第二種是[[類名 alloc]init]。這兩種方法是等價(jià)的,不過(guò),通常的Cocoa慣例是使用alloc和init,而不使用new.一般情況下,Cocoa程序員只是在他們不具備足夠的水平來(lái)熟練使用alloc和init方法時(shí),才將new作為輔助方法使用。
[[類名alloc]init]有兩個(gè)動(dòng)作。alloc是分配動(dòng)作,是從操作系統(tǒng)獲得一塊內(nèi)存并將其指定為存放對(duì)象的實(shí)例變量的位置。同時(shí),alloc方法還將這塊內(nèi)存區(qū)域全部初始化為0。與分配動(dòng)作對(duì)應(yīng)的是初始化。有如下兩種初始化寫法。
Car *car =[[Class alloc] init];
寫法1
Car*car = [Car alloc];
[carinit];
寫法2
應(yīng)該使用第一種寫法,因?yàn)閕nit返回的對(duì)象可能不是以前的那個(gè)。
2.編寫初始化方法
下面是一段初始化的代碼
-(id)init
{
if(self = [superinit]){
engine = [Enginenew];
…
}
}
使用self= [super init]的作用是使超類完成它們自己的初始化工作。同時(shí)因?yàn)閕nit可能返回的是不同的對(duì)象,實(shí)例變量所在的內(nèi)存位置到隱藏的self參數(shù)之間的跳離又是固定的,所以要這樣使用。
注:這部分可以參考書[1]144頁(yè)。
第六節(jié):協(xié)議
這里的協(xié)議是正式協(xié)議,相對(duì)的還有非正式協(xié)議,這在類別一節(jié)中有介紹。正式協(xié)議是一個(gè)命名的方法列表。它要求顯式地采用協(xié)議。采用協(xié)議意味著要實(shí)現(xiàn)協(xié)議的所有方法。否則,編譯器會(huì)通過(guò)生成警告來(lái)提醒你。
1.聲明協(xié)議
@protocolNSCopying
-(id)copyWithZone:(NSZone*)zone;
@end
2.采用協(xié)議
@interface Car :NSObject
{
// instancevariables
}
@end
協(xié)議可以采用多個(gè),并且可以按任意順序列出這些協(xié)議,沒有什么影響。
3.OC 2.0的新特性
OC2.0增加了兩個(gè)新的協(xié)議修飾符:@optional和@required,因此你可以像下面這樣編寫代碼:
@protocolBaseballPlayer
-(void)drawHugeSalary;
@optional
-(void)slideHome;
-(void)catchBall;
@required
-(void)swingBat;
@end
因此,一個(gè)采用BaseballPlayer協(xié)議的類有兩個(gè)要求實(shí)現(xiàn)的方法:-drawHugeSalary和-swingBat,還有3個(gè)不可選擇實(shí)現(xiàn)的方法:slideHome,catchBall和throwBall。
第七節(jié):委托
Cocoa中的類經(jīng)常使用一種名為委托(delegate)的技術(shù),委托是一種對(duì)象,另一個(gè)類的對(duì)象會(huì)要求委托對(duì)象執(zhí)行它的某些操作。常用的是,編寫委托 對(duì)象并將其提供給其他一些對(duì)象,通常是提供給Cocoa生成的對(duì)象。通過(guò)實(shí)現(xiàn)特定的方法,你可以控制Cocoa中的對(duì)象的行為。
通過(guò)下面的例子,可以更清楚地理解委托的實(shí)現(xiàn)原理。其中A對(duì)象需要把一些方法委托給其它對(duì)象來(lái)實(shí)現(xiàn),例子中就是對(duì)象B,B實(shí)現(xiàn)了含A對(duì)象特定方法的協(xié)議ADelegate,從而可以在B中實(shí)現(xiàn)A委托的方法。
@protocolADelegate
-(void)aDelegateMethod;
……
@end
@interfaceA : NSObject {
……
iddelegate;
}
@property(readwrite, assign)
iddelegate;
……
@end
@implementationA
@synthesizedelegate;
-(void)aMethod{
[delegateaDelegateMethod];
……
}
@end
A類
@interfaceB : NSObject
@end
@implementationB
-(id)init {
……
[[AsharedA] setDelegate:self];
}
-(void)aDelegateMethod{//B中實(shí)現(xiàn)A委托的方法
……
}
…
@end
B類
注:實(shí)現(xiàn)委托還可以使用類別,在第八節(jié)中將做介紹
第八節(jié): 類別
類別允許你在現(xiàn)有的類中加入新功能,這些類可以是框架中的類,并且不需要擴(kuò)充它。
1.聲明類別
@interfaceNSString (NumberConvenience)
-(NSNumber *)lengthAsNumber;
@end
該聲明表示,類別的名稱是NumberConvenience,而且該類別將向NSString類中添加方法。
2.實(shí)現(xiàn)類別
@implementationNSString (NumberConvenience)
-(NSNumber*) lengthAsNumber
{
unsigned intlength = [self length];
return([NSNumber numberWithUnsignedInt: length]);
}
@end
3.局限性
類別有兩方面的局限性。第一,無(wú)法向類中添加新的實(shí)例變量。類別沒有位置容納實(shí)例變量。第二,名稱沖突,即類別中的方法與現(xiàn)有的方法重名。當(dāng)發(fā)生名稱沖突時(shí),類別具有更高的優(yōu)先級(jí)。這點(diǎn)可以通過(guò)增加一個(gè)前綴的方法解決。
4.非正式協(xié)議和委托類別
實(shí)現(xiàn)委托除了第七節(jié)中應(yīng)用協(xié)議的方式,還可以使用類別。具體做法就是把委托對(duì)象要實(shí)現(xiàn)的方法聲明為一個(gè)NSObject的類別。如下面的代碼所示:
@interfaceNSObject(NSSomeDelegateMethods)
-(void)someMethod;
…
@end
通過(guò)將這些方法聲明為NSObject的類別,使得只要對(duì)象實(shí)現(xiàn)了委托方法,任何類的對(duì)象都可以成為委托對(duì)象。創(chuàng)建一個(gè)NSObject的類別稱為“創(chuàng)建 一個(gè)非正式協(xié)議”。非正式協(xié)議只是一種表達(dá)方式,它表示“這里有一些你可能想實(shí)現(xiàn)的方法”,第六節(jié)介紹的協(xié)議可以叫做正式協(xié)議。
非正式協(xié)議的作用類似于使用許多@optional的正式協(xié)議,并且前者正逐漸被后者所代替。
5.選擇器
選擇器只是一個(gè)方法名稱,它以O(shè)C運(yùn)行時(shí)使用的特殊方式編碼,以快速執(zhí)行查詢。你可以使用@selector()預(yù)編譯指令指定選擇器,其中方法名位于圓括號(hào)中。如一個(gè)類中setEngine:方法的選擇器是:@selector(setEngine:)。
因?yàn)檫x擇器可以被傳遞,可以作為方法的參數(shù)使用,甚至可以作為實(shí)例變量存儲(chǔ)。這樣可以生成一些非常強(qiáng)大和靈活的構(gòu)造。
第九節(jié):Posing
Posing有點(diǎn)像類別,但不太一樣。它允許你擴(kuò)充一個(gè)類,并且全面性地扮演(pose)這個(gè)超類。例如:你有一個(gè)擴(kuò)充NSArry的 NSArrayChild對(duì)象。如果你讓NSArrayChild扮演NSArry,則在你的代碼中所有的NSArray都會(huì)自動(dòng)被替代為 NSArrayChild.
@interfaceFractionB: Fraction
-(void)print;
@end
@implementationFractionB
-(void)print {
printf("(%i/%i)", numerator, denominator );
}
@end
Fraction.m
intmain( int argc, const char *argv[] ) {
Fraction*frac = [[Fraction alloc] initWithNumerator: 3 denominator: 10];
//print it
printf("The fraction is: " );
[fracprint];
printf("\n" );
//make FractionB pose as Fraction
[FractionBposeAsClass: [Fraction class]];
Fraction*frac2 = [[Fraction alloc] initWithNumerator: 3 denominator: 10];
//print it
printf("The fraction is: " );
[frac2print];
printf("\n" );
//free memory
[fracrelease];
[frac2release];
return0;
}
Main.m
Thefraction is: 3/10
Thefraction is: (3/10)
輸出
這個(gè)程序的輸出中,第一個(gè)fraction會(huì)輸出3/10,而第二個(gè)會(huì)輸出(3/10),這是FractionB中實(shí)現(xiàn)的方式。poseAsClass這個(gè)方法是NSObject的一部分,它允許子類扮演超類。
第十節(jié):動(dòng)態(tài)識(shí)別 (Dynamictypes)
下面是應(yīng)用動(dòng)態(tài)識(shí)別時(shí)所用到的方法:
-(BOOL)isKindOfClass:classObj
是否是其子孫或一員
-(BOOL)isMemberOfClass:classObj
是否是其一員
-(BOOL)respondsToSelector:selector
是否有這種方法
+(BOOL)instancesRespondToSelector:selector
類的對(duì)象是否有這種方法
-(id)performSelector:selector
執(zhí)行對(duì)象的方法
通過(guò)下面的代碼可以更清楚地理解動(dòng)態(tài)類型的使用:
import"Square.h"
#import"Rectangle.h"
#import
“intmain( int argc, const char *argv[] ) {
Rectangle*rec = [[Rectangle alloc] initWithWidth: 10 height: 20];
Square*sq = [[Square alloc] initWithSize: 15];
//isMemberOfClass
//true
if( [sq isMemberOfClass: [Square class]] == YES ) {
printf("square is a member of square class\n" );
}
//false
if( [sq isMemberOfClass: [Rectangle class]] == YES ) {
printf("square is a member of rectangle class\n" );
}
//false
if( [sq isMemberOfClass: [NSObject class]] == YES ) {
printf("square is a member of object class\n" );
}
//isKindOfClass
//true
if( [sq isKindOfClass: [Square class]] == YES ) {
printf("square is a kind of square class\n" );
}
//true
if( [sq isKindOfClass: [Rectangle class]] == YES ) {
printf("square is a kind of rectangle class\n" );
}
//true
if( [sq isKindOfClass: [NSObject class]] == YES ) {
printf("square is a kind of object class\n" );
}
//respondsToSelector
//true
if( [sq respondsToSelector: @selector( setSize: )] == YES ) {
printf("square responds to setSize: method\n" );
}
//false
if( [sq respondsToSelector: @selector( nonExistant )] == YES ) {
printf("square responds to nonExistant method\n" );
}
//true
if( [Square respondsToSelector: @selector( alloc )] == YES ) {
printf("square class responds to alloc method\n" );
}
//instancesRespondToSelector
//false
if( [Rectangle instancesRespondToSelector: @selector( setSize: )] ==YES ) {
printf("rectangle instance responds to setSize: method\n" );
}
//true
if( [Square instancesRespondToSelector: @selector( setSize: )] ==YES ) {
printf("square instance responds to setSize: method\n" );
}
//free memory
[recrelease];
[sqrelease];
return0;
}”
輸出:
squareis a member of square class
squareis a kind of square class
squareis a kind of rectangle class
squareis a kind of object class
squareresponds to setSize: method
squareclass responds to alloc method
squareinstance responds to setSize: method
第十一節(jié):參考內(nèi)容與延伸閱讀
[1]:《LearnObjective-C on the Mac》MarkDalrymple , Scott Knaster
[2]:《Programmingin Objective-C》SteveKochan
[3]:http://www.otierney.net/objective-c.html.en
[4]:http://cocoadevcentral.com/d/learn_objectivec/