Objective-C基礎文法快速入門

來源:互聯網
上載者:User

標籤:objective-c   objective-c文法   物件導向   mac   軟體開發   

Objective-C是Mac軟體開發領域最主要的開發語言,假如我們對C語言已經很熟悉或者具有物件導向語言的基礎,對於我們學習Objective-C將會非常有用。


方法調用(Calling Methods)
為了能夠儘快上手,我們先來看一些簡單的例子。Objective-C文法裡面基本的方法調用是這樣的:


[object method];  
 
[object methodWithInput:input];  
 
對象的方法可以返回值:


output = [object methodWithOutput];  
 
output = [object methodWithInputAndOutput:input];  
 
我們也可以在類裡面調用如何建立對象的方法。下面的這個例子裡面,我們調用了NSString類的string方法:


id myObject = [NSString string];  
 
id的類型意味著myObject這個變數可以指向任意類型的變數。當我們編譯這個應用程式的時候,並不知道他實現的真實的類和方法。


在這個例子裡面,很明顯這個對象的類型應該是NSString,所以我們可以改一下他的類型:


NSString* myString = [NSString string];  
 
現在myString就是一個NSString類型的變數。這個時候假如我們試圖使用一個NSString沒有實現的方法時,編譯器就會警告我們。


一定要注意在物件類型的右邊有一個星號。所有的Objective-C物件變數都是指標類型的。id類型已經預先被定義成一個指標類型了。所以我們不需要再加星號。


嵌套訊息調用(Nested Messages)
在許多程式設計語言裡面嵌套訊息,或者嵌套函數看起來就像這樣:


function1 ( function2() );  
function2的返回值被傳遞給function1當輸入參數。在Objective-C裡面,嵌套訊息調用就像這樣:


[NSString stringWithFormat:[prefs format]];  
我們應該盡量避免在一行代碼裡面嵌套調用超過兩個。因為這樣的話,代碼的可讀性就不太好。


多參輸入的方法(Multi-Input Methods)
多個輸入參數的方法。在Objective-C裡面,一個方法名可以被分割成幾段。在標頭檔裡面,就應該這樣子來定義一個多輸入參數的方法:


-(BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;  
 
我們這樣來調用它:


BOOL result = [myData writeToFile:@"/tmp/log.txt" atomically:NO];  
 
參數不一定要給它命名。在運行期系統裡面這個方法真實的名字叫writeToFile:atomically:。


Accessors(Getter & Setter)  
 
在Objective-C裡面所有的執行個體對象預設都是私人的。所有在大多數情況下我們需要用accessors去讀取或者設定變數的值。有兩個文法都支援這樣的操作,這個時傳統的老的文法:


[photo setCaption:@"Day at the Beach"];  
output = [photo caption];  
 
第二行的代碼其實並非直接去讀對象執行個體的變數。事實上它調用的是名叫caption的方法。在Objective-C裡大多數情況下我們不需要給getters加get的首碼。


無論什麼時候我們見到方括弧,其實我們都是向一個對象或者一個類發送了一個訊息。


Dot Syntax
在Objective-C 2.0裡面,新增加了一個"."操作的文法。在Mac OS X 10.5裡面就使用了Objective-C 2.0文法:


photo.caption = @"Day at the Beach";  
output = photo.caption;  
 
我們兩種方式都可以使用。但是在一個工程裡面最好保持風格一致,只使用某一種。"."操作只能夠被使用在setters和getters裡面,而不能用在一般意思的方法上。


建立對象
主要有兩種方式來建立一個對象。第一種辦法像這面這樣:


NSString* myString = [NSString string];  
 
這是一種非常習慣性的風格。在這種方式情況下,我們建立的是系統自動釋放(autoreleased)類型的對象。關於自動釋放類型autoreleased,我們以後會深入討論一下。然而在許多情況下,我們需要手動的去建立對象:


NSString* myString = [[NSString alloc] init];  
 
這是一個嵌套的方法調用。第一個調用的NSString自己的alloc方法。這是一個相對比較底層的調用,因為他建立了內容,以及執行個體化了一個對象。


第二塊代碼調用了新建立對象的init方法。這個init方法實現了比較常用的基本設定,比如建立執行個體對象的參數。對於一般開發人員而言,實現這個客戶的類的具體的細節並不清楚。


在一些情況下,我們可以用不通的初始化方式去賦初值:


NSNumber* value = [[NSNumber alloc] initWithFloat:1.0];  
 
基本的記憶體管理
假如我們正在為Mac OS X開發一個應用程式,我們可以選擇是否啟用記憶體回收機制。這就意味著我們不需要去考慮記憶體管理,除了一個特別複雜的情形我們需要處理一下。


然而,我們有的時候我們的開發環境沒有記憶體回收機制,比如iPhone開發的時候就沒有記憶體回收機制。在這種情況下,我們就需要瞭解一些基本的記憶體管理方面的概念。


假如我們手動的通過alloc建立了一個對象,我們需要用完這個對象後release它。我們不需要手動的去release一個autoreleased類型的對象,假如真的這樣去做的話,我們的應用程式將會crash。


這裡有兩個例子:


// string1 will be released automatically  
 
NSString* string1 = [NSString string];  
 
// must release this when done  
 
NSString* string2 = [[NSString alloc] init];  
 
[string2 release];  
 
就這個教程而言,我們可以人為autoreleased對象會在當前函數方法調用完成後被釋放。


當然了,還有很多關於記憶體管理的只是我們需要學習,但是這需要我們瞭解更多的基本概念以後才能去涉及。


設計一個類的Interface
就Objective-C語言而言,建立一個類非常簡單。它非常典型的分成了兩個部分。


類的介面通常儲存在ClassName.h檔案裡,它定義了執行個體的參數,以及一些公開的方法。


類的實現在ClassName.m檔案裡。它包含了真正啟動並執行代碼和那些方法。它還經常定義一些私人的方法。這些私人的方法對於子類是不可見的。


這裡有一個介面檔案的大概。類名Photo,所以檔案名稱叫Photo.h:


#import  
 
@interface Photo : NSObject {  
 
NSString* caption;  
 
NSString* photographer;  
 
}  
 
@end  
 
首先,我們把Cocoa.h import進來。Cocoa的應用程式的所有的基本的類大多都是這樣做的。#import巨集指令會自動的避免把同一個檔案包含多次。


@interface符號表明這是Photo類的聲明。冒號指定了父類。上面這個例子父類就是NSObject。


在大括弧裡面,有兩個變數:caption和photographer。兩個都是NSString類型的。當然了,他們也可以是任何別的類型包括id類型的。


最後@end結束整個聲明。


增加方法
讓我們為成員變數加一些getters:


#import  
 
@interface Photo : NSObject {  
 
NSString* caption;  
 
NSString* photographer;  
 
}  
 
- caption;  
 
- photographer;  
 
@end  
 
別忘記,Objective-C方法不需要加get首碼。一個單獨小橫杆表明它是一個執行個體的方法。假如是一個加號的話,那就說明它是一個類的方法。


編譯器預設的方法的傳回型別為id。還有所有的方法的參數的預設類型也都是id類型的。所以上面的代碼從技術上講是對的。但是很少這麼用。我們還是給它加上傳回型別吧:


#import  
 
@interface Photo : NSObject {  
 
NSString* caption;  
 
NSString* photographer;  
 
}  
 
- (NSString*) caption;  
 
- (NSString*) photographer;  
 
@end  
 
下面我們再加上setters:


#import  
 
@interface Photo : NSObject {  
 
NSString* caption;  
 
NSString* photographer;  
 
}  
 
- (NSString*) caption;  
 
- (NSString*) photographer;  
 
- (void) setCaption: (NSString*)input;  
 
- (void) setPhotographer: (NSString*)input;  
 
@end  
 
Setters不需要返回任何值,所以我們把它的類型指定為void.


類的實現
我們通過實現getters來建立一個類的實現:


#import "Photo.h"  
 
@implementation Photo  
 
- (NSString*) caption {  
 
return caption;  
 
}  
 
- (NSString*) photographer {  
 
return photographer;  
 
}  
 
@end  
 
這部分的代碼由@implementation再來加上類名開始,以@end結束。就跟類的介面定義一樣,所有的方法跟介面定義裡的一樣。所有的對象都必要既要定義也要實現。


假如我們以前也寫過代碼的話,Objective-C裡面的getters看上去跟別的差不多。所以我們下面就來介紹setters,它需要一點說明。


- (void) setCaption: (NSString*)input  
 
{  
 
[caption autorelease];  
 
caption = [input retain];  
 
}  
 
- (void) setPhotographer: (NSString*)input  
 
{  
 
[photographer autorelease];  
 
photographer = [input retain];  
 
}  
 
每個setter處理兩個變數。第一個是當前存在對象的應用。第二個是新的輸入對象。在支援記憶體回收的開發環境裡,我們只要直接賦新值就可以了:


- (void) setCaption: (NSString*)input {  
 
caption = input;  
 
}  
 
但是假如我們不可以用記憶體回收機制的話,我們就需要先retain舊的對象,然後retain新的對象。


有兩種方法可以釋放一個引用對象:release 和 autorelease。標準的release會直接刪除引用。autorelease方法會在將來的某個時候去release它。在它聲明周期結束前,它會毫無疑問的存在。在本例中,上面setPhotographer中的photographer對象,將會在函數結束的時候被釋放。


在setter裡面用autorelease是安全的,因為新對象跟老的對象有可能是同一個對象有可能指向的是同一個對象。對於一個我們即將retain的對象,我們不應該立即release它。


這個也許現在看起來會困惑,但是隨著我們的學習,會越來越能理解它。現在我們不需要立刻完全理解它。


初始化
我們可以建立一個初始化方法去給類的執行個體的成員變數賦初值:


- (id) init  
 
{  
 
if ( self = [super init] )  
 
{  
 
[self setCaption:@"Default Caption"];  
 
[self setPhotographer:@"Default Photographer"];  
 
}  
 
return self;  
 
}  
 
上面的代碼感覺沒啥好解釋的,雖然第二行代碼好像看上去沒啥用。這個是一個單等號,就是把[super init]的值賦給了self。


它基本上是在調用父類去實現它的初始化。這個if程式碼片段是設定預設值之前驗證初始化是否成功。


釋放資源Dealloc
這個dealloc方法是在當一個對象希望被從內容裡面刪除的時候調用。這個我們釋放在子類裡面引用成員變數的最好的時機:


- (void) dealloc  
 
{  
 
[caption release];  
 
[photographer release];  
 
[super dealloc];  
 
}  
 
開始兩行我們發送了release通知給了兩個成員變數。我們不要在這裡用autorelease。用標準的release更快一點。


最後一行的[super dealloc];非常重要。我們必須要發送訊息去讓父類清除它自己。假如不這麼做的話,這個對象其實沒有被清除乾淨,存在記憶體泄露。


dealloc在記憶體回收機制下不會被調用到。取而代之的是,我們需要實現finalize方法。


更多關於記憶體管理
Objective-C的記憶體管理系統基於引用記數。所有我們需要關心的就是跟蹤我們引用,以及在運行期內是否真的釋放了記憶體。


用最簡單的術語來解釋,當我們alloc一個對象的時候,應該在某個時候retain了它。每次我們調用了alloc或者retain之後,我們都必須要調用release。


這就是引用記數理論。但是在實踐的時候,只有兩種情況我們需要建立一個對象:


1. 成為一個類的成員變數


2. 只臨時的在一個函數裡面被使用


在更多的時候,一個成員變數的setter應該僅僅autorelease舊的對象,然後retain新的對象。我們只需要在dealloc的時候調用release就可以了。


所以真正需要做的就是管理函數內部的local的引用。唯一的原則就是:假如我們alloc或者copy了一個對象,那麼我們在函數結束的時候需要release或者autorelease它。假如我們是通過別的方式建立的,就不管。


這裡是管理成員對象的例子:


- (void) setTotalAmount: (NSNumber*)input  
 
{  
 
[totalAmount autorelease];  
 
totalAmount = [input retain];  
 
}  
 
- (void) dealloc  
 
{  
 
[totalAmount release];  
 
[super dealloc];  
 
}  
 
這裡是本地引用的例子。我們只需要release我們用alloc建立的對象:


NSNumber* value1 = [[NSNumber alloc] initWithFloat:8.75];  
 
NSNumber* value2 = [NSNumber numberWithFloat:14.78];  
 
// only release value1, not value2  
 
[value1 release];  
 
這裡是用本地引用對象去設一個成員變數的例子:


NSNumber* value1 = [[NSNumber alloc] initWithFloat:8.75];  
 
[self setTotal:value1];  
 
NSNumber* value2 = [NSNumber numberWithFloat:14.78];  
 
[self setTotal:value2];  
 
[value1 release];  
 
注意到如何管理本地引用其實都是一樣的。不管你是否把它設給了一個成員變數。我們無須考慮setters的內部實現。


如果我們很好的理解了這些的話,我們基本上理解了80%的Objective-C記憶體管理方面的內容了。


屬性Properties
前面我們寫caption和author的accessors的時候,你可以已經注意到了代碼非常簡明,應該可以被抽象提取出來。


屬性在Objective-C裡是一個新的功能。他可以讓我們自動的產生accessors,另外還有一些別的優點。我們可以把上面Photo的類轉成用屬性來實現:


上面那個類原先的實現是這樣:


#import  
 
@interface Photo : NSObject {  
 
NSString* caption;  
 
NSString* photographer;  
 
}  
 
- (NSString*) caption;  
 
- (NSString*) photographer;  
 
- (void) setCaption: (NSString*)input;  
 
- (void) setPhotographer: (NSString*)input;  
 
@end  
 
假如用屬性來實現就是這樣:


#import  
 
@interface Photo : NSObject {  
 
NSString* caption;  
 
NSString* photographer;  
 
}  
 
@property (retain) NSString* caption;  
 
@property (retain) NSString* photographer;  
 
@end  
 
@property是Objective-C來聲明屬性的編譯指令。括弧裡面的"retain"指明了setter需要retain輸入的對象。這行其他的部分指定了屬性的類型以及名字。


下面讓我們來看看這個類的實現:


#import "Photo.h"  
 
@implementation Photo  
 
@synthesize caption;  
 
@synthesize photographer;  
 
- (void) dealloc  
 
{  
 
[caption release];  
 
[photographer release];  
 
[super dealloc];  
 
}  
 
@end  
 
@synthesize指令自動的產生了我們的setters和getters。所以我們只需要實作類別的dealloc方法。


Accessors只有當他們原先沒有的時候,才會被產生。所以可以放心大膽的去用@synthesize來指定屬性。而且可以隨意實現你自己的getter和setter。編譯器會自己去找哪個方法沒有。


屬性聲明還有別的選項,但是限於篇幅層次,我們下次再介紹。


Logging


在Objective-C裡,往console寫日記非常簡單。事實上NSLog()跟C語言的printf()兩個函數幾乎完全相同,除了NSLog是用額外的“%@”去獲得對象。


NSLog ( @"The current date and time is: %@", [NSDate date] );  
 
我們可以log一個對象到console裡去。NSLog函數調用要輸出對象的description方法,然後列印返回的NSString。我們可以在自己的類裡重寫description方法,這樣我們就可以得到一個自訂的字串。


調用nil對象的方法(Calling Methods on Nil)
在Objective-C裡,nil對象被設計來跟NULLnull 指標關聯的。他們的區別就是nil是一個對象,而NULL只是一個值。而且我們對於nil調用方法,不會產生crash或者拋出異常。


這個技術被framework通過多種不同的方式使用。最主要的就是我們現在在調用方法之前根本無須去檢查這個對象是否是nil。假如我們調了nil對象的一個有返回值的方法,那麼我們會得到一個nil返回值。


我們可以通過nil對象讓我們的dealloc函數實現看上去更好一些:


- (void) dealloc  
{  
self.caption = nil;  
self.photographer = nil;  
[super dealloc];  
}  
 
之所以可以這麼做是因為我們給把nil對象設給了一個成員變數,setter就會retain nil對象(當然了這個時候nil對象啥事情也不會做)然後release舊的對象。這個方式來釋放對象其實更好,因為這樣做的話,成員變數連指向隨機資料的機會都沒有,而通過別的方式,出現指向隨機資料的情形機會不可避免。


注意到我們調用的self.VAR這樣的文法,這表示我們正在用setter,而且不會引起任何記憶體問題。假如我們直接去設值的話,就會有記憶體溢出:


// incorrect. causes a memory leak.  
// use self.caption to go through setter  
caption = nil;  
 
Categories
Categories是Objective-C裡面最常用到的功能之一。 基本上category可以讓我們給已經存在的類增加方法,而不需要增加一個子類。而且不需要知道它內部具體的實現。


如果我們想增加某個framework內建的類的方法,這非常有效。如果我們想在我們程式工程的NSString能夠增加一個方法,我們就可以使用category。甚至都不需要自己實現一個NSString的子類。


比如,我們想在NSString裡面增加一個方法來判斷它是否是一個URL,那我們就可以這麼做:


#import  
 
@interface NSString (Utilities)  
 
- (BOOL) isURL;  
 
@end  
 
這跟類的定義非常類似。區別就是category沒有父類,而且在括弧裡面要有category的名字。名字可以隨便取,但是習慣叫法會讓人比較明白category裡面有些什麼功能的方法。


這裡是具體的實現。但是要注意,這本身並不是一個判斷URL很好的實現。我們主要是為了整體的瞭解category的概念。


#import "NSString-Utilities.h"  
 
@implementation NSString (Utilities)  
 
- (BOOL) isURL  
 
{  
 
if ( [self hasPrefix:@"http://"] )  
 
return YES;  
 
else  
 
return NO;  
 
}  
 
@end  
 
現在我們可以在任何的NSString類對象裡都可以調用這個方法了。下面的代碼在console裡面列印的"string1 is a URL":


NSString* string1 = @"http://www.CocoaDev.cn/";  
 
NSString* string2 = @"Pixar";  
 
if ( [string1 isURL] )  
 
NSLog (@"string1 is a URL");  
 
if ( [string2 isURL] )  
 
NSLog (@"string2 is a URL");  
 
跟子類不一樣,category不能增加成員變數。我們還可以用category來重寫類原先的存在的方法,但是這需要非常非常小心。


記住,當我們通過category來修改一個類的時候,它對應用程式裡的這個類所有對象都起作用。


後記
上面Objective-C的比較基礎的大概的講了一下。Objective-C還是比較好上手的。沒有特別的文法需要去學習。而且一些概念在Objective-C裡面被反覆運用。

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

Objective-C基礎文法快速入門

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.