Objective-C 程式設計
| 請更正此作品的標題,使其符合 Wikibooks 的命名策略。 您可以透過重新命名所有受影響的頁面和連結來提供幫助,包括此頁面。 您也可以分享您的想法,或在專案室尋求幫助。 |
有一些關鍵的Objective-C概念與一般面向物件程式設計實踐密切相關。目前,我們不會先看確切的語法。
- 如果您有面向物件程式設計的經驗,您可能希望跳過這些部分並檢視總結部分。
Objective-C 中的面向物件與 C++ 有些相似之處,它是一種像 C 樣式結構體一樣組織記憶體的方式,不同之處在於,它期望結構體包含指向函式的指標,這些函式可以引用任何定義為例項變數或函式指標的偏移量,因此物件是一個記憶體位置,從中可以找到例項變數的偏移量,並且有一個指向類定義的指標,該指標提供了類函式的地址。
在 C++ 中,要從類的成員函式呼叫成員函式,語法是
this -> func ( a, b );
或
(*this).func (a, b);
這表示指向“this”物件的指標,即定義了“this”正在使用的函式的類的例項。
在 Objective-C 中,'self' 是用來替換 'this' 的關鍵字,呼叫方式如下:
[self func: a, b ]
因此,這裡函式的引數不像大多數其他基於 C 的語言那樣用括號括起來,而是
在子類的建構函式中,當需要呼叫父類的建構函式時,Java 中的建構函式是這樣呼叫的。
Child( a, b, c ) { super(a); this.b = b; this.c = c; }
類似地,在 Objective-C 中,對於類介面
@interface MyClass : MyParentClass {
MyClassB * b;
int c;
}
@property (copy, nonatomic) NSInteger d;
-(void) func1; -(void) func2: (int)param1, (id) param2; @end
存在一個省略的 init 建構函式,以及
建構函式可能是
- (id) init: (id)a, (id) b, (int) c , (NSInteger) d {
self = [super init:a ]; // -(2)
if (self) {
_b = [[B alloc] initByCopy:b] ;
_c = c;
self.d = d;
}
return self;
}
並且存在 MyParentClass 的建構函式,它是
- (id) init: (id) a // - (2a)
{ .. }
上面的例子展示了
- id 是類型別的佔位符。
- 在函式宣告或定義中,通常將括號放在返回值型別周圍,以及每個引數之前以給出它們的型別。
- self 是一個提供的例項變數,它引用類的一個物件,但最初可以是超型別的物件,如 (2) 和 (2a) 中所示。
- 約定是呼叫 alloc,它是類函式,用於讓類獲取一個物件,然後透過呼叫一個物件函式來初始化該物件,該函式通常是 'init' 或以 'init..' 開頭,所以模式是 my_object = [[A_Class alloc] init:a_param1, a_param2],這意味著“為由 A_Class 模板化的物件分配記憶體,然後使用 a_param1 和 a_param2 呼叫物件的 init”。
- 一些簡單的 C 語言型別,如 int、double、char*,可以用以 NS 開頭的更復雜的類來表示。
- 在上面的示例中,_b 和 _c 沒有在其前面使用 self,但 d 有。d 在類的介面宣告部分定義為屬性。屬性具有省略的用於例項變數的 set/get 函式,並且可以具有屬性屬性,如 (copy, nonatomic) - copy 表示“self.d = d”是“self.d = [ [NSInteger alloc] initByCopy:d ]”的簡寫,其中 NSInteger 是 d 的類。
- 記住 Objective-C 是 C 的擴充套件,變數名通常指代一個由 4、8 或 16 個位元組組成的短序列,這些位元組要麼表示數字(int、long、float、double、char),要麼表示地址(char*、類例項指標)。這有點不像 Java,在 Java 中,變數是對物件的引用,物件的引用方式以及它們的記憶體管理方式是省略或隱藏的。Objective-C 框架後來在 NS.. 類家族中添加了引用計數自動釋放記憶體管理。
Objective C 中可以使用 C 中可用的任何標準 Unix 庫,因此包括對檔案輸入/輸出、動態 (malloc/free) 分配、字串函式、數學函式、程序建立、同步以及透過檔案鎖、執行緒鎖、執行緒條件變數、訊號量、管道、套接字、共享記憶體管理、訊號進行程序間通訊等服務的標準系統介面。但是,Objective C 允許將資料結構與方法分組,並使用面向物件的模式(如多型和委託(透過 Objective-C 的“協議”功能))來組織結構化程式設計的快捷方式。其他功能還包括自動引用計數,以便動態分配的物件在沒有垃圾收集的情況下自動釋放。這導致了標準作業系統介面函式 (malloc/free) 被標準 Objective C. 面向物件的習慣用法所取代,例如來自 NSObject 的習慣用法,例如
NS<some class>* p = [[NS<some class> alloc] init] ;
對於熟悉點語法(例如 Java)的人來說,這意味著與
MYClass p = (MyClass.alloc()).init();
“呼叫靜態工廠類方法 alloc(),並在物件上呼叫 init()”。
在 C 中,只有
MyStruct * p = malloc ( size of(MyStruct));
稍後,指標 p 可能會與另一個指標(比如 q)共享記憶體地址,並且會有 2 個引用,然後如果稍後 q = z,甚至更晚 p = z,但是等等,存在記憶體洩漏,因此必須在 p 上呼叫 free(),因為它是對上面 malloc 呼叫生成的記憶體分配的最後一個引用。
free(p);
在舊的 Objective-C 框架中,
[p dealloc];
然而,自動引用計數或 ARC,意味著當 p = z 時,在上面的示例中,p 指向的 NS 物件的引用計數降至零,並且會在 ARC 支援框架持有的隱藏指標上呼叫 free()。
示例 2,而不是
char * charbuf[maxlength]; snprintf ( charbuf, maxlength-1, "hello %s , %d times!", "world", 2);
有
NSString* mystrbuf = [[NSString alloc] initWithFormat: "hello %s , %d times!", "world", 2];
面向物件程式設計(通常簡稱為 OO 或 OOP)是一種程式設計正規化,其中程式碼以稱為類的單元編寫,其中與資料結構相關的函式在其中宣告和定義。例如,ANSI C 在結構體s 中,類似地,OO 語言將資料儲存在類或物件中,但還聲明瞭對結構化資料(物件)的例項進行操作的函式。類本身可以是一個物件,具有相關資料和類函式,但每個程序空間只有一個例項。(一般來說,作業系統為每個正在執行的程式分配一個程序,但允許多程序程式,這些程式是啟動其他程式的程式)。
例如,如果您正在建立文字處理器,您可以透過一個物件來表示文件,該物件以文字作為其資料,並與它關聯不同的任務,以對文件的資料執行操作,例如“拼寫檢查”。
然後,文字處理器將是一個可能繪製視窗並顯示文件文字的小程式。拼寫檢查按鈕將在該文件的物件上執行“拼寫檢查”任務。
但是,如果我們同時編輯兩個文件,它們在文件內容(資料)方面可能會有所不同。但所有文件或多或少都是一樣的:每個文件都包含一個用於其文字的空間,並且具有幾個定義在文字上執行操作的任務。每個文件物件本質上具有相同的形式。我們將此形式稱為物件的類,我們經常為它命名。讓我們將文件類命名為文件(樣式說明:我們通常將類的名稱大寫)。
現在,每個文件物件都具有文件的形式。我們說每個文件物件都是文件的一個例項。因此,例如,我們的文字處理器在文件類的例項上執行操作。有時我們可能會將文件類的例項簡稱為“一個 文件".
請記住,每個物件都包含一些資料。例如,對於文件類來說,每個文件都有資料來儲存文件的文字。我們把每個物件持有的資料稱為例項變數。因此,一個文件可以指定一個例項變數來儲存文字,或一個例項變數來儲存字數、作者或我們可能想要指定的任何其他資料。每個文件都有自己的例項變數,與其他任何文件的例項變數是分開的。
每個物件還可以執行一些針對其例項變數的任務。在 Objective-C 中,我們稱這些任務為方法。物件的方 法在其物件的例項變數上操作。
然而,物件不僅僅如此。使用 Objective-C,我們可以用物件做很多有趣的事情。
使用物件
[edit | edit source]讓我們繼續以我們的文字處理程式為例。假設我們想要建立一個新的文件例項,以便我們可以使用它。這個過程稱為例項化。
現在我們已經擁有了一個活動的物件例項,我們可以透過使用物件的方法來使用它。我們將在稍後學習定義和使用方法。每個方法都像 C 中的函式一樣,在 Objective-C 中,語法只稍有變化。
當我們完全使用完物件並想要擺脫它(以便它不再佔用記憶體)時,我們會執行所謂的物件銷燬或釋放。這就像free一個malloc的陣列,例如在 C 中。但是,如果在使用物件方法的過程中,我們為其例項變數分配了一些額外的記憶體,我們必須釋放這部分記憶體。
物件技術
[edit | edit source]當我們建立新類時,我們可能希望基於之前建立的類來建立新物件的的行為。例如,如果我們想要建立一個包含樣式資訊的文件類,我們可能希望基於之前建立的`Document`類來建立這個類。
這是一個非常常見的特性,它被包含在面向物件系統中,被稱為繼承。當一個類從另一個類繼承時,新類將採用舊類的形式,並且可以新增額外的例項變數和方法。
因此,如果我們想要建立一個`StyledDocument`類,我們將會從`Document`類繼承。在 Objective-C 中,一個類只能從另一個類繼承。這被稱為單繼承。
介面和實現
[edit | edit source]就像在現實世界中一樣,物件的使用方式和物件工作方式之間存在分離。例如,使用我們的文字處理程式,拼寫檢查器有一個一致的介面(如何使用它),例如跳轉到下一個拼寫錯誤的單詞、後退和更正的按鈕。但在幕後,拼寫檢查器的工作方式(實現)可以用很多不同的方式完成。
在 Objective-C 中,介面和實現是分開的。這使得不同的物件能夠以一種明確定義的方式協同工作和連結。例如,為了改變電視的頻道,你會使用電視機上按鈕的介面,而不是開啟它並在電子元件上亂動。
將實現與介面分開意味著我們可以更改實現中的任何內容(甚至用其他實現替換它),而所有內容都應該仍然可以工作,因為物件(按鈕等)之間只通過它們的介面進行互動。
總結
[edit | edit source]因此,在學習 Objective-C 語法之前,讓我們回顧一下
- 一個物件是一個類的例項
- 它定義了被稱為方法的任務
- 以及被稱為例項變數的資料
- 它定義了被稱為方法的任務
- 一個類可以從另一個類(單繼承)繼承(並且只能從一個類繼承)