初學(xué)者必學(xué)文檔:Objective-C語(yǔ)法入門(mén)
Objective-C語(yǔ)法入門(mén)是本文要介紹的內(nèi)容,Objective-C是Mac軟件開(kāi)發(fā)領(lǐng)域最主要的開(kāi)發(fā)語(yǔ)言。假如我們對(duì)面向?qū)ο蟮乃季S已經(jīng)C語(yǔ)言都很熟悉的話,對(duì)于我們學(xué)習(xí)Objective-C將會(huì)非常有用。假如我們對(duì)C語(yǔ)言還不熟悉的話,那我們需要學(xué)習(xí)一下C語(yǔ)言。
方法調(diào)用(Calling Methods)
為了能夠盡快上手,我們先來(lái)看一些簡(jiǎn)單的例子。Objective-C語(yǔ)法里面基本的方法調(diào)用是這樣的:
程序代碼:
- [object method];
- [object methodWithInput:input];
對(duì)象的方法可以返回值:
程序代碼:
- output = [object methodWithOutput];
- output = [object methodWithInputAndOutput:input];
我們也可以在類(lèi)里面調(diào)用如何創(chuàng)建對(duì)象的方法。下面的這個(gè)例子里面,我們調(diào)用了NSString類(lèi)的string方法:
- id myObject = [NSString string];
id的類(lèi)型意味著myObject這個(gè)變量可以指向任意類(lèi)型的變量。當(dāng)我們編譯這個(gè)應(yīng)用程序的時(shí)候,并不知道他實(shí)現(xiàn)的真實(shí)的類(lèi)和方法。
在這個(gè)例子里面,很明顯這個(gè)對(duì)象的類(lèi)型應(yīng)該是NSString,所以我們可以改一下他的類(lèi)型
- NSString* myString = [NSString string];
現(xiàn)在myString就是一個(gè)NSString類(lèi)型的變量。這個(gè)時(shí)候假如我們?cè)噲D使用一個(gè)NSString沒(méi)有實(shí)現(xiàn)的方法時(shí),編譯器就會(huì)警告我們。
一定要注意在對(duì)象類(lèi)型的右邊有一個(gè)星號(hào)。所有的Objective-C對(duì)象變量都是指針類(lèi)型的。id類(lèi)型已經(jīng)預(yù)先被定義成一個(gè)指針類(lèi)型了。所以我們不需要再加星號(hào)。
嵌套消息調(diào)用(Nested Messages)
在許多編程語(yǔ)言里面嵌套消息,或者嵌套函數(shù)看起來(lái)就像這樣:
- function1 ( function2() );
function2的返回值被傳遞給function1當(dāng)輸入?yún)?shù)。在Objective-C里面,嵌套消息調(diào)用就像這樣:
- [NSString stringWithFormat:[prefs format]];
我們應(yīng)該盡量避免在一行代碼里面嵌套調(diào)用超過(guò)兩個(gè)。因?yàn)檫@樣的話,代碼的可讀性就不太好。
多參輸入的方法(Multi-Input Methods)
多個(gè)輸入?yún)?shù)的方法。在Objective-C里面,一個(gè)方法名可以被分割成幾段。在頭文件里面,就應(yīng)該這樣子來(lái)定義一個(gè)多輸入?yún)?shù)的方法:
- -(BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;
我們這樣來(lái)調(diào)用它:
- BOOL result = [myData writeToFile:@"/tmp/log.txt" atomically:NO];
參數(shù)不一定要給它命名。在運(yùn)行期系統(tǒng)里面這個(gè)方法真實(shí)的名字叫writeToFile:atomically:。
- Accessors(Getter & Setter)
在Objective-C里面所有的實(shí)例對(duì)象默認(rèn)都是私有的。所有在大多數(shù)情況下我們需要用accessors去讀取或者設(shè)置變量的值。有兩個(gè)語(yǔ)法都支持這樣的操作,這個(gè)時(shí)傳統(tǒng)的老的語(yǔ)法:
- [photo setCaption:@"Day at the Beach"];
- output = [photo caption];
第二行的代碼其實(shí)并非直接去讀對(duì)象實(shí)例的變量。事實(shí)上它調(diào)用的是名叫caption的方法。在Objective-C里大多數(shù)情況下我們不需要給getters加get的前綴。
無(wú)論什么時(shí)候我們見(jiàn)到方括號(hào),其實(shí)我們都是向一個(gè)對(duì)象或者一個(gè)類(lèi)發(fā)送了一個(gè)消息。
- Dot Syntax
在bjective-C 2.0里面,新增加了一個(gè)"."操作的語(yǔ)法。在Mac OS X 10.5里面就使用了Objective-C 2.0語(yǔ)法:
- photo.caption = @"Day at the Beach";
- output = photo.caption;
我們兩種方式都可以使用。但是在一個(gè)工程里面最好保持風(fēng)格一致,只使用某一種。"."操作只能夠被使用在setters和getters里面,而不能用在一般意思的方法上。
#p#
創(chuàng)建對(duì)象
主要有兩種方式來(lái)創(chuàng)建一個(gè)對(duì)象。第一種辦法像這面這樣:
- NSString* myString = [NSString string];
這是一種非常習(xí)慣性的風(fēng)格。在這種方式情況下,我們創(chuàng)建的是系統(tǒng)自動(dòng)釋放(autoreleased)類(lèi)型的對(duì)象。關(guān)于自動(dòng)釋放類(lèi)型autoreleased,我們以后會(huì)深入討論一下。然而在許多情況下,我們需要手動(dòng)的去創(chuàng)建對(duì)象:
- NSString* myString = [[NSString alloc] init];
這是一個(gè)嵌套的方法調(diào)用。第一個(gè)調(diào)用的NSString自己的alloc方法。這是一個(gè)相對(duì)比較底層的調(diào)用,因?yàn)樗麆?chuàng)建了內(nèi)容,以及實(shí)例化了一個(gè)對(duì)象。
第二塊代碼調(diào)用了新創(chuàng)建對(duì)象的init方法。這個(gè)init方法實(shí)現(xiàn)了比較常用的基本設(shè)置,比如創(chuàng)建實(shí)例對(duì)象的參數(shù)。對(duì)于一般開(kāi)發(fā)人員而言,實(shí)現(xiàn)這個(gè)客戶的類(lèi)的具體的細(xì)節(jié)并不清楚。
在一些情況下,我們可以用不通的初始化方式去賦初值:
- NSNumber* value = [[NSNumber alloc] initWithFloat:1.0];
基本的內(nèi)存管理
假如我們正在為Mac OS X開(kāi)發(fā)一個(gè)應(yīng)用程序,我們可以選擇是否啟用垃圾回收機(jī)制。這就意味著我們不需要去考慮內(nèi)存管理,除了一個(gè)特別復(fù)雜的情形我們需要處理一下。
然而,我們有的時(shí)候我們的開(kāi)發(fā)環(huán)境沒(méi)有垃圾回收機(jī)制,比如iPhone開(kāi)發(fā)的時(shí)候就沒(méi)有垃圾回收機(jī)制。在這種情況下,我們就需要了解一些基本的內(nèi)存管理方面的概念。
假如我們手動(dòng)的通過(guò)alloc創(chuàng)建了一個(gè)對(duì)象,我們需要用完這個(gè)對(duì)象后release它。我們不需要手動(dòng)的去release一個(gè)autoreleased類(lèi)型的對(duì)象,假如真的這樣去做的話,我們的應(yīng)用程序?qū)?huì)crash。
這里有兩個(gè)例子:
程序代碼:
- // string1 will be released automatically
- NSString* string1 = [NSString string];
- // must release this when done
- NSString* string2 = [[NSString alloc] init];
- [string2 release];
就這個(gè)教程而言,我們可以人為autoreleased對(duì)象會(huì)在當(dāng)前函數(shù)方法調(diào)用完成后被釋放。
當(dāng)然了,還有很多關(guān)于內(nèi)存管理的只是我們需要學(xué)習(xí),但是這需要我們了解更多的基本概念以后才能去涉及。
設(shè)計(jì)一個(gè)類(lèi)的Interface
就Objective-C語(yǔ)言而言,創(chuàng)建一個(gè)類(lèi)非常簡(jiǎn)單。它非常典型的分成了兩個(gè)部分。
類(lèi)的接口通常保存在ClassName.h文件里,它定義了實(shí)例的參數(shù),以及一些公開(kāi)的方法。
類(lèi)的實(shí)現(xiàn)在ClassName.m文件里。它包含了真正運(yùn)行的代碼和那些方法。它還經(jīng)常定義一些私有的方法。這些私有的方法對(duì)于子類(lèi)是不可見(jiàn)的。
這里有一個(gè)接口文件的大概。類(lèi)名Photo,所以文件名叫Photo.h:
程序代碼:
- #import
- @interface Photo : NSObject {
- NSString* caption;
- NSString* photographer;
- }
- @end
首先,我們把Cocoa.h import進(jìn)來(lái)。Cocoa的應(yīng)用程序的所有的基本的類(lèi)大多都是這樣做的。#import宏指令會(huì)自動(dòng)的避免把同一個(gè)文件包含多次。
@interface符號(hào)表明這是Photo類(lèi)的聲明。冒號(hào)指定了父類(lèi)。上面這個(gè)例子父類(lèi)就是NSObject。
在大括弧里面,有兩個(gè)變量:caption和photographer。兩個(gè)都是NSString類(lèi)型的。當(dāng)然了,他們也可以是任何別的類(lèi)型包括id類(lèi)型的。
最后@end結(jié)束整個(gè)聲明。
增加方法
讓我們?yōu)槌蓡T變量加一些getters:
程序代碼:
- #import
- @interface Photo : NSObject {
- NSString* caption;
- NSString* photographer;
- }
- - caption;
- - photographer;
- @end
別忘記,Objective-C方法不需要加get前綴。一個(gè)單獨(dú)小橫桿表明它是一個(gè)實(shí)例的方法。假如是一個(gè)加號(hào)的話,那就說(shuō)明它是一個(gè)類(lèi)的方法。
編譯器默認(rèn)的方法的返回類(lèi)型為id。還有所有的方法的參數(shù)的默認(rèn)類(lèi)型也都是id類(lèi)型的。所以上面的代碼從技術(shù)上講是對(duì)的。但是很少這么用。我們還是給它加上返回類(lèi)型吧:
程序代碼:
- #import
- @interface Photo : NSObject {
- NSString* caption;
- NSString* photographer;
- }
- - (NSString*) caption;
- - (NSString*) photographer;
- @end
下面我們?cè)偌由蟬etters:
程序代碼:
- #import
- @interface Photo : NSObject {
- NSString* caption;
- NSString* photographer;
- }
- - (NSString*) caption;
- - (NSString*) photographer;
- - (void) setCaption: (NSString*)input;
- - (void) setPhotographer: (NSString*)input;
- @end
Setters不需要返回任何值,所以我們把它的類(lèi)型指定為void.
#p#
類(lèi)的實(shí)現(xiàn)
我們通過(guò)實(shí)現(xiàn)getters來(lái)創(chuàng)建一個(gè)類(lèi)的實(shí)現(xiàn):
程序代碼:
- #import "Photo.h"
- @implementation Photo
- - (NSString*) caption {
- return caption;
- }
- - (NSString*) photographer {
- return photographer;
- }
- @end
這部分的代碼由@implementation再來(lái)加上類(lèi)名開(kāi)始,以@end結(jié)束。就跟類(lèi)的接口定義一樣,所有的方法跟接口定義里的一樣。所有的對(duì)象都必要既要定義也要實(shí)現(xiàn)。
假如我們以前也寫(xiě)過(guò)代碼的話,Objective-C里面的getters看上去跟別的差不多。所以我們下面就來(lái)介紹setters,它需要一點(diǎn)說(shuō)明。
程序代碼:
- - (void) setCaption: (NSString*)input
- {
- [caption autorelease];
- caption = [input retain];
- }
- - (void) setPhotographer: (NSString*)input
- {
- [photographer autorelease];
- photographer = [input retain];
- }
每個(gè)setter處理兩個(gè)變量。第一個(gè)是當(dāng)前存在對(duì)象的應(yīng)用。第二個(gè)是新的輸入對(duì)象。在支持垃圾回收的開(kāi)發(fā)環(huán)境里,我們只要直接賦新值就可以了:
程序代碼:
- - (void) setCaption: (NSString*)input {
- caption = input;
- }
但是假如我們不可以用垃圾回收機(jī)制的話,我們就需要先retain舊的對(duì)象,然后retain新的對(duì)象。
有兩種方法可以釋放一個(gè)引用對(duì)象:release 和 autorelease。標(biāo)準(zhǔn)的release會(huì)直接刪除引用。autorelease方法會(huì)在將來(lái)的某個(gè)時(shí)候去release它。在它聲明周期結(jié)束前,它會(huì)毫無(wú)疑問(wèn)的存在。在本例中,上面setPhotographer中的photographer對(duì)象,將會(huì)在函數(shù)結(jié)束的時(shí)候被釋放。
在setter里面用autorelease是安全的,因?yàn)樾聦?duì)象跟老的對(duì)象有可能是同一個(gè)對(duì)象有可能指向的是同一個(gè)對(duì)象。對(duì)于一個(gè)我們即將retain的對(duì)象,我們不應(yīng)該立即release它。
這個(gè)也許現(xiàn)在看起來(lái)會(huì)困惑,但是隨著我們的學(xué)習(xí),會(huì)越來(lái)越能理解它。現(xiàn)在我們不需要立刻完全理解它。
初始化
我們可以創(chuàng)建一個(gè)初始化方法去給類(lèi)的實(shí)例的成員變量賦初值:
程序代碼:
- - (id) init
- {
- if ( self = [super init] )
- {
- [self setCaption:@"Default Caption"];
- [self setPhotographer:@"Default Photographer"];
- }
- return self;
上面的代碼感覺(jué)沒(méi)啥好解釋的,雖然第二行代碼好像看上去沒(méi)啥用。這個(gè)是一個(gè)單等于號(hào),就是把[super init]的值賦給了self。
它基本上是在調(diào)用父類(lèi)去實(shí)現(xiàn)它的初始化。這個(gè)if代碼段是設(shè)置默認(rèn)值之前驗(yàn)證初始化是否成功。
釋放資源Dealloc
這個(gè)dealloc方法是在當(dāng)一個(gè)對(duì)象希望被從內(nèi)容里面刪除的時(shí)候調(diào)用。這個(gè)我們釋放在子類(lèi)里面引用成員變量的最好的時(shí)機(jī):
程序代碼:
- - (void) dealloc
- {
- [caption release];
- [photographer release];
- [super dealloc];
- }
開(kāi)始兩行我們發(fā)送了release通知給了兩個(gè)成員變量。我們不要在這里用autorelease。用標(biāo)準(zhǔn)的release更快一點(diǎn)。
最后一行的[super dealloc];非常重要。我們必須要發(fā)送消息去讓父類(lèi)清除它自己。假如不這么做的話,這個(gè)對(duì)象其實(shí)沒(méi)有被清除干凈,存在內(nèi)存泄露。
dealloc在垃圾回收機(jī)制下不會(huì)被調(diào)用到。取而代之的是,我們需要實(shí)現(xiàn)finalize方法。
- More on Memory Management
Objective-C的內(nèi)存管理系統(tǒng)基于引用記數(shù)。所有我們需要關(guān)心的就是跟蹤我們引用,以及在運(yùn)行期內(nèi)是否真的釋放了內(nèi)存。
用最簡(jiǎn)單的術(shù)語(yǔ)來(lái)解釋?zhuān)?dāng)我們alloc一個(gè)對(duì)象的時(shí)候,應(yīng)該在某個(gè)時(shí)候retain了它。每次我們調(diào)用了alloc或者retain之后,我們都必須要調(diào)用release。
///// 。。。圖。。。。
這就是引用記數(shù)理論。但是在實(shí)踐的時(shí)候,只有兩種情況我們需要?jiǎng)?chuàng)建一個(gè)對(duì)象:
1. 成為一個(gè)類(lèi)的成員變量
2. 只臨時(shí)的在一個(gè)函數(shù)里面被使用
在更多的時(shí)候,一個(gè)成員變量的setter應(yīng)該僅僅autorelease舊的對(duì)象,然后retain新的對(duì)象。我們只需要在dealloc的時(shí)候調(diào)用release就可以了。
所以真正需要做的就是管理函數(shù)內(nèi)部的local的引用。唯一的原則就是:假如我們alloc或者copy了一個(gè)對(duì)象,那么我們?cè)诤瘮?shù)結(jié)束的時(shí)候需要release或者autorelease它。假如我們是通過(guò)別的方式創(chuàng)建的,就不管。
這里是管理成員對(duì)象的例子:
程序代碼:
- - (void) setTotalAmount: (NSNumber*)input
- {
- [totalAmount autorelease];
- totalAmount = [input retain];
- }
- - (void) dealloc
- {
- [totalAmount release];
- [super dealloc];
- }
這里是本地引用的例子。我們只需要release我們用alloc創(chuàng)建的對(duì)象:
程序代碼:
- NSNumber* value1 = [[NSNumber alloc] initWithFloat:8.75];
- NSNumber* value2 = [NSNumber numberWithFloat:14.78];
- // only release value1, not value2
- [value1 release];
這里是用本地引用對(duì)象去設(shè)一個(gè)成員變量的例子:
程序代碼:
- NSNumber* value1 = [[NSNumber alloc] initWithFloat:8.75];
- [self setTotal:value1];
- NSNumber* value2 = [NSNumber numberWithFloat:14.78];
- [self setTotal:value2];
- [value1 release];
注意到如何管理本地引用其實(shí)都是一樣的。不管你是否把它設(shè)給了一個(gè)成員變量。我們無(wú)須考慮setters的內(nèi)部實(shí)現(xiàn)。
如果我們很好的理解了這些的話,我們基本上理解了80%的Objective-C內(nèi)存管理方面的內(nèi)容了。
小結(jié):初學(xué)者必學(xué)文檔:Objective-C語(yǔ)法入門(mén)的內(nèi)容介紹完了,希望本文對(duì)你有所幫助!