[Objective-C] 程式設計教學:如何建立和使用物件 (Objects)

【分享本文】
Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

    前言

    Objective-C 的特色就是在 C 加上物件系統。此外,Cocoa 或 GNUstep 帶來立即可用的物件庫。在本文中,我們會介紹如何建立和使用現有的 Objective-C 物件。

    Objective-C 物件的基本在訊息傳遞

    Objective-C 最基本的物件語法如下:

    [object message];
    

    上述語法類似於 C++ 的方法呼叫:

    object->method();
    

    但在 Objective-C 中,我們會說傳遞 message (訊息) 給 object,而不會說 object 呼叫某方法 (method),這是因為 Objective-C 物件的行為和 C++ (或 Java) 物件的行為相異而造成的,這無法單純從語法看出來。我們現在先學用法,最後再來談其運行期行為。

    我們延伸上述語法,加入參數:

    [object message];
    [object messageWith: object_1];
    [object messageWithFirst: object_1 andSecond: object_2];
    

    當我們要傳入帶有參數的訊息時,會使用 :;傳入的參數也是另一個物件。如果要傳入兩個以上的參數時,會用第三行的語法;這代表我們同時傳入兩個訊息,每個訊息各帶有一個參數。

    上述語法相當於以下的 C++ 程式碼:

    object->method();
    object->methodWith(object_1);
    object->methodWith(object_1, object_2);
    

    由此可知,Objective-C 不使用參數列 (parameter lists),而是將每個參數分別以不同 (帶有參數的) 訊息傳入物件中。

    建立和使用物件

    這行敘述建立一個 NSInteger 物件:

    NSInteger i = [NSNumber numberWithInteger: 345];
    

    在這裡,我們宣告變數 i,其型別是 NSInteger,我們使用 [NSNumber numberWithInteger: 345] 建立新的 NSInteger 物件。

    如果讀者去查詢 NSNumber 的文件,會發現 NSNumber 是類別 (class)。在 Objective-C 中,類別本身也視為物件,所以我們可以直接使用相同的語法來建立物件,而不需用額外的保留字,像是 C++ 或 Java 的 new

    在 Objective-C 中,大部分的物件的型別是指標型別,如下例:

    NSFileHandle *stdin = [NSFileHandle fileHandleWithStandardInput];
    

    在這裡,我們宣告變數 stdinstdin 是指標 (pointer),指向 NSFileHandle 型別。stdin 物件會從標準輸入 (standard input) 接收資料。

    那為什麼先前的 NSInteger 不是指標呢?因為 NSIntegerlongint 的別名,是基礎型別 (參考 NSInteger 的文件)。

    我們來看一個接收標準輸入的範例程式碼:

    // Create a file handle for STDIN
    NSFileHandle *input = [NSFileHandle fileHandleWithStandardInput];
    
    // Get some data from the file handler
    NSData *data = [input availableData];
        
    // Get string from the raw data and trim trailing spaces from it
    NSString* str = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] \
        stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    

    在第一行程式中,我們建立 input 物件。在第二行,我們向 input 物件傳遞 availableData 的訊息,這時會回傳 data 物件。至於第三行比較複雜,我們先把第三行拆成三行來寫:

    // Allocate memory for `str`
    NSString *str = [NSString alloc];
    
    // Init `str` object with `data`
    str = [str initWithData:data encoding:NSUTF8StringEncoding];
    
    // Trim the trailing spaces of `str`
    str = [str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    

    一開始,我們為 str 物件配置記憶體。接著,我們以先前的 data 物件將 str 物件初始化,在初始化時指定編碼 (encoding)。最後,將多餘的空白字元去除 (trim)。原來的第三行是把三個步驟併在一起寫,所以看起來會比較長。

    Objective-C 是動態語言

    我們先前有介紹過 id 這個 Objective-C 的萬用型別,只要該變數是 Objective-C 物件即可。我們將先前的型別註解全部改成 id

    id input = [NSFileHandle fileHandleWithStandardInput];
    id data = [input availableData];
    id str = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] \
        stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    

    但是程式仍然可以正確編譯和執行,這是因為訊息傳遞和方法呼叫在本質上的不同。方法呼叫和類別緊密結合,在編譯期就會知道某個方法在該物件中是否存在。相對來說,訊息傳遞在執行期時,才會確認物件是否可回應特定訊息。

    那麼,我們為什麼不把全部的型別都用 id 取代呢?確實地標註型別,有利於程式的編譯,因為 Objective-C 編譯器會根據型別標註來查找某物件是否可執行某訊息。此外,清楚地標註訊型別的資訊也有利於維護程式碼。

    由於 Objective-C 在本質上是動態語言,寫起來必較靈活。靈活所帶來的代價是較難優化。所以,當程式的效能很重要時,最好還是用 C 或 C++ 來寫,再視需求加上 Objective-C 的 binding。

    結語

    在本文中,我們談到如何使用 Objective-C 的物件,但我們沒有談到如何管理這些物件的記憶體。我們會在下一篇文章中討論 Objective-C 的記憶體管理模式。此外,我們目前還不會自己寫新的類別,這在後續的文章會介紹。

    【分享本文】
    Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email
    【追蹤新文章】
    Facebook Twitter Plurk