objective-c中文教程

開始吧

下載這篇教學

設定環境

為了編譯 GNUstep 應用程式,必須先執行位於 /usr/GNUstep/System/Makefiles/GNUstep.sh 的 GNUstep.sh 這個檔案。這個路徑取決於你的系統環境,有些是在 /usr, some /usr/lib,有些是 /usr/local。如果你的 shell 是以 csh/tcsh 為基礎的 shell,則應該改用 GNUStep.csh。建議把這個指令放在 .bashrc 或 .cshrc 中。

前言

(譯注:台灣出版書名為 C 程式語言第二版)這是 C 語言的設計者所寫的書。

編譯 hello world


 所有這篇初學者指南的原始碼都可以由
objc.tar.gz 下載。這篇教學中的許多範例都是由 Steve Kochan 在
Programming in Objective-C
 . 一書中撰寫。如果你想得到更多詳細資訊及範例,請直接參考該書。這個網站上登載的所有範例皆經過他的允許,所以請勿複製轉載。

創建 classes

@interface

@implementation


把它們湊在一起



基於 "Programming in Objective-C," Copyright © 2004 by Sams Publishing 一書中的範例,並經過允許而刊載。 Fraction.h
#import 

@interface Fraction: NSObject {
    int numerator;
    int denominator;
}

-(void) print;
-(void) setNumerator: (int) d;
-(void) setDenominator: (int) d;
-(int) numerator;
-(int) denominator;
@end
NSObject:NeXTStep Object 的縮寫。因為它已經改名為 OpenStep,所以這在今天已經不是那麼有意義了。 繼承(inheritance)以 Class: Parent 表示,就像上面的 Fraction: NSObject。 夾在 @interface Class: Parent { .... } 中的稱為 instance variables。 沒有設定存取權限(protected, public, private)時,預設的存取權限為 protected。設定權限的方式將在稍後說明。 Instance methods 跟在成員變數(即 instance variables)後。格式為:scope (returnType) methodName: (parameter1Type) parameter1Name; scope 有class 或 instance 兩種。instance methods 以 - 開頭,class level methods 以 + 開頭。 Interface 以一個 @end 作為結束。

詳細說明...

多重參數

建構子(Constructors)





存取權限





Class level access





異常情況(Exceptions)








 目前為止我還沒展示如何傳遞多個參數。這個語法乍看之下不是很直覺,不過它卻是來自一個十分受歡迎的 Smalltalk 版本。
基於 "Programming in Objective-C," Copyright © 2004 by Sams Publishing一書中的範例,並經過允許而刊載。 Fraction.h
...
-(void) setNumerator: (int) n andDenominator: (int) d;
...
Fraction.m
...
-(void) setNumerator: (int) n andDenominator: (int) d {
    numerator = n;
    denominator = d;
}
...
main.m
#import 
#import "Fraction.h"

int main( int argc, const char *argv[] ) {
    // create a new instance
    Fraction *frac = [[Fraction alloc] init];
    Fraction *frac2 = [[Fraction alloc] init];

    // set the values
    [frac setNumerator: 1];
    [frac setDenominator: 3];

    // combined set
    [frac2 setNumerator: 1 andDenominator: 5];

    // print it
    printf( "The fraction is: " );
    [frac print];
    printf( "/n" );

    // print it
    printf( "Fraction 2 is: " );
    [frac2 print];
    printf( "/n" );

    // free memory
    [frac release];
    [frac2 release];

    return 0;
}
output
The fraction is: 1/3
Fraction 2 is: 1/5
這個 method 實際上叫做 setNumerator:andDenominator: 加入其他參數的方法就跟加入第二個時一樣,即 method:label1:label2:label3: ,而呼叫的方法是 [obj method: param1 label1: param2 label2: param3 label3: param4] Labels 是非必要的,所以可以有一個像這樣的 method:method:::,簡單的省略 label 名稱,但以 : 區隔參數。並不建議這樣使用。

繼承、多型(Inheritance, Polymorphism)以及其他物件導向功能

id 型別

繼承(Inheritance)







動態識別(Dynamic types)



Categories









Posing





Protocols







 Objective-C 有種叫做 id 的型別,它的運作有時候像是 void*,不過它卻嚴格規定只能用在物件。Objective-C 與 Java 跟 C++ 不一樣,你在呼叫一個物件的 method 時,並不需要知道這個物件的型別。當然這個 method 一定要存在,這稱為 Objective-C 的訊息傳遞。
基於 "Programming in Objective-C," Copyright © 2004 by Sams Publishing一書中的範例,並經過允許而刊載。 Fraction.h
#import 

@interface Fraction: NSObject {
    int numerator;
    int denominator;
}

-(Fraction*) initWithNumerator: (int) n denominator: (int) d;
-(void) print;
-(void) setNumerator: (int) d;
-(void) setDenominator: (int) d;
-(void) setNumerator: (int) n andDenominator: (int) d;
-(int) numerator;
-(int) denominator;
@end
Fraction.m
#import "Fraction.h"
#import 

@implementation Fraction
-(Fraction*) initWithNumerator: (int) n denominator: (int) d {
    self = [super init];

    if ( self ) {
        [self setNumerator: n andDenominator: d];
    }

    return self;
}

-(void) print {
    printf( "%i / %i", numerator, denominator );
}

-(void) setNumerator: (int) n {
    numerator = n;
}

-(void) setDenominator: (int) d {
    denominator = d;
}

-(void) setNumerator: (int) n andDenominator: (int) d {
    numerator = n;
    denominator = d;
}

-(int) denominator {
    return denominator;
}

-(int) numerator {
    return numerator;
}
@end
Complex.h
#import 

@interface Complex: NSObject {
    double real;
    double imaginary;
}

-(Complex*) initWithReal: (double) r andImaginary: (double) i;
-(void) setReal: (double) r;
-(void) setImaginary: (double) i;
-(void) setReal: (double) r andImaginary: (double) i;
-(double) real;
-(double) imaginary;
-(void) print;

@end
Complex.m
#import "Complex.h"
#import 

@implementation Complex
-(Complex*) initWithReal: (double) r andImaginary: (double) i {
    self = [super init];

    if ( self ) {
        [self setReal: r andImaginary: i];
    }

    return self;
}

-(void) setReal: (double) r {
    real = r;
}

-(void) setImaginary: (double) i {
    imaginary = i;
}

-(void) setReal: (double) r andImaginary: (double) i {
    real = r;
    imaginary = i;
}

-(double) real {
    return real;
}

-(double) imaginary {
    return imaginary;
}

-(void) print {
    printf( "%_f + %_fi", real, imaginary );
}

@end
main.m
#import 
#import "Fraction.h"
#import "Complex.h"

int main( int argc, const char *argv[] ) {
    // create a new instance
    Fraction *frac = [[Fraction alloc] initWithNumerator: 1 denominator: 10];
    Complex *comp = [[Complex alloc] initWithReal: 10 andImaginary: 15];
    id number;

    // print fraction
    number = frac;
    printf( "The fraction is: " );
    [number print];
    printf( "/n" );

    // print complex
    number = comp;
    printf( "The complex number is: " );
    [number print];
    printf( "/n" );

    // free memory
    [frac release];
    [comp release];

    return 0;
}
output
The fraction is: 1 / 10
The complex number is: 10.000000 + 15.000000i
這種動態連結有顯而易見的好處。你不需要知道你呼叫 method 的那個東西是什麼型別,如果這個物件對這個訊息有反應,那就會喚起這個 method。這也不會牽涉到一堆繁瑣的轉型動作,比如在 Java 裡呼叫一個整數物件的 .intValue() 就得先轉型,然後才能呼叫這個 method。

記憶體管理

到目前為止我都刻意避開 Objective-C 的記憶體管理議題。你可以呼叫物件上的 dealloc,但是若物件裡包含其他物件的指標的話,要怎麼辦呢?要釋放那些物件所佔據的記憶體也是一個必須關注的問題。當你使用 Foundation framework 建立 classes 時,它如何管理記憶體?這些稍後我們都會解釋。 注意:之前所有的範例都有正確的記憶體管理,以免你混淆。

Retain and Release(保留與釋放)

Dealloc





Autorelease Pool






 Retain 以及 release 是兩個繼承自 NSObject 的物件都會有的 methods。每個物件都有一個內部計數器,可以用來追蹤物件的 reference 個數。如果物件有 3 個 reference 時,不需要 dealloc 自己。但是如果計數器值到達 0 時,物件就得 dealloc 自己。[object retain] 會將計數器值加 1(值從 1 開始),[object release] 則將計數器值減 1。如果呼叫 [object release] 導致計數器到達 0,就會自動 dealloc。 Fraction.m
...
-(void) dealloc {
    printf( "Deallocing fraction/n" );
    [super dealloc];
}
...
基於 "Programming in Objective-C," Copyright © 2004 by Sams Publishing一書中的範例,並經過允許而刊載。 main.m
#import "Fraction.h"
#import 

int main( int argc, const char *argv[] ) {
    Fraction *frac1 = [[Fraction alloc] init];
    Fraction *frac2 = [[Fraction alloc] init];

    // print current counts
    printf( "Fraction 1 retain count: %i/n", [frac1 retainCount] );
    printf( "Fraction 2 retain count: %i/n", [frac2 retainCount] );

    // increment them
    [frac1 retain]; // 2
    [frac1 retain]; // 3
    [frac2 retain]; // 2

    // print current counts
    printf( "Fraction 1 retain count: %i/n", [frac1 retainCount] );
    printf( "Fraction 2 retain count: %i/n", [frac2 retainCount] );

    // decrement
    [frac1 release]; // 2
    [frac2 release]; // 1

    // print current counts
    printf( "Fraction 1 retain count: %i/n", [frac1 retainCount] );
    printf( "Fraction 2 retain count: %i/n", [frac2 retainCount] );
    
    // release them until they dealloc themselves
    [frac1 release]; // 1
    [frac1 release]; // 0
    [frac2 release]; // 0
}
output
Fraction 1 retain count: 1
Fraction 2 retain count: 1
Fraction 1 retain count: 3
Fraction 2 retain count: 2
Fraction 1 retain count: 2
Fraction 2 retain count: 1
Deallocing fraction
Deallocing fraction
Retain call 增加計數器值,而 release call 減少它。你可以呼叫 [obj retainCount] 來取得計數器的 int 值。 當當 retainCount 到達 0,兩個物件都會 dealloc 自己,所以可以看到印出了兩個 "Deallocing fraction"。

Foundation framework classes

Foundation framework 地位如同 C++ 的 Standard Template Library。不過 Objective-C 是真正的動態識別語言(dynamic types),所以不需要像 C++ 那樣肥得可怕的樣版(templates)。這個 framework 包含了物件組、網路、執行緒,還有更多好東西。

NSArray

NSDictionary



基於 "Programming in Objective-C," Copyright © 2004 by Sams Publishing一書中的範例,並經過允許而刊載。 main.m
#import 
#import 
#import 
#import 
#import 

void print( NSArray *array ) {
    NSEnumerator *enumerator = [array objectEnumerator];
    id obj;

    while ( obj = [enumerator nextObject] ) {
        printf( "%s/n", [[obj description] cString] );
    }
}

int main( int argc, const char *argv[] ) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSArray *arr = [[NSArray alloc] initWithObjects:
                    @"Me", @"Myself", @"I", nil];
    NSMutableArray *mutable = [[NSMutableArray alloc] init];

    // enumerate over items
    printf( "----static array/n" );
    print( arr );

    // add stuff
    [mutable addObject: @"One"];
    [mutable addObject: @"Two"];
    [mutable addObjectsFromArray: arr];
    [mutable addObject: @"Three"];

    // print em
    printf( "----mutable array/n" );
    print( mutable );

    // sort then print
    printf( "----sorted mutable array/n" );
    [mutable sortUsingSelector: @selector( caseInsensitiveCompare: )];
    print( mutable );
    
    // free memory
    [arr release];
    [mutable release];
    [pool release];

    return 0;
}
output
----static array
Me
Myself
I
----mutable array
One
Two
Me
Myself
I
Three
----sorted mutable array
I
Me
Myself
One
Three
Two
陣列有兩種(通常是 Foundation classes 中最資料導向的部分),NSArray 跟 NSMutableArray,顧名思義,mutable(善變的)表示可以被改變,而 NSArray 則不行。這表示你可以製造一個 NSArray 但卻不能改變它的長度。 你可以用 Obj, Obj, Obj, ..., nil 為參數呼叫建構子來初始化一個陣列,其中 nil 表示結尾符號。 排序(sorting)展示如何用 selector 來排序一個物件,這個 selector 告訴陣列用 NSString 的忽略大小寫順序來排序。如果你的物件有好幾個排序方法,你可以使用這個 selector 來選擇你想用的方法。 在 print method 裡,我使用了 description method。它就像 Java 的 toString,會回傳物件的 NSString 表示法。 NSEnumerator 很像 Java 的列舉系統。while ( obj = [array objectEnumerator] ) 行得通的理由是 objectEnumerator 會回傳最後一個物件的 nil。在 C 裡 nil 通常代表 0,也就是 false。改用 ( ( obj = [array objectEnumerator] ) != nil ) 也許更好。

你可能感兴趣的:(iPhone版块)