Objective-C入門(一)

Objective-C與C語言的關係

Objective-C是C語言的嚴格超集,完全兼容C,C語言編寫的程序可以不經修改直接被Objective-C編譯。

Objective-C的文件擴展名

擴展名 內容類型
.h 頭文件。可以包含類,類型,函數和常數的聲明
.m 源文件。可以同時包含Objective-C和C代碼
.mm 源文件。可以同時包含Objective-C、C代碼、C++代碼

用來包含頭文件的編譯選項#include,在OC中則建議使用#import,和#include的區別是,#import可以確保相同的文件只會被包含一次。

方法調用格式:

[obj method:argument]

逐個解釋:

obj是類的實現對象;

method是要調用的方法;

argument是方法參數;

方法調用必須用[]括起來。

消息傳遞

OC語言,消息傳遞模型,對象之間互相傳遞消息。

C++類別和方法嚴格清楚,一個方法必定屬於一個類別,編譯時已經綁定關係,不可能調用一個不存在類裏面的方法。

OC類別和消息(方法)的關係鬆散,調用方法視爲對對象發送消息,所有方法都被視爲對消息的迴應。所有消息處理直到運行時纔會動態綁定,並交由類別自行決定如何處理收到的消息。一個類別不保證一定會迴應收到的消息,如果類別收到了一個無法處理的消息,程序只會拋出異常,不會出錯或崩潰。

[car fly];

發一個fly的信息給car,fly是消息,而car是消息的接收者。car收到消息後會決定如何迴應這個消息,若car類別內定義有fly方法就運行方法內之代碼,若car內不存在fly方法,則程序依舊可以通過編譯,運行期則拋出異常。

字符串

OC語言中使用一個字符串類型來接受字符串,NSString

NSString* myString = @"My String\n";
NSString* anotherString = [NSString stringWithFormat:@"%d %s", 1, @"String"];

// 從一個C語言字符串創建Objective-C字符串
NSString*  fromCString = [NSString stringWithCString:"A C string" 
encoding:NSASCIIStringEncoding];

下圖展現了聲明一個叫做 MyClass 的類的語法,這個類繼承自 NSObject 基礎類。類聲明總是由 @interface 編譯選項開始,由 @end 編譯選項結束。類名之後的(用冒號分隔的)是父類的名字。類的實例(或者成員)變量聲明在被大括號包含的代碼塊中。實例變量塊後面就是類聲明的方法的列表。每個實例變量和方法聲明都以分號結尾。

類的定義文件遵循C語言之慣例以.h爲後綴,實現文件以.m爲後綴。

圖片來自www.runoob.com

Interface

@interface MyObject : NSObject {
    int memberVar1; // 實體變量
    id  memberVar2;
}

+(return_type) class_method; // 類方法

-(return_type) instance_method1; // 實例方法
-(return_type) instance_method2: (int) p1;
-(return_type) instance_method3: (int) p1 andPar: (int) p2;
@end

方法前面的 +/- 號代表函數的類型:加號(+)代表類方法(class method),不需要實例就可以調用,與C++ 的靜態函數(static member function)相似。減號(-)即是一般的實例方法(instance method)。

Objective-C定義一個新的方法時,名稱內的冒號(:)代表參數傳遞,不同於C語言以數學函數的括號來傳遞參數。Objective-C方法使得參數可以夾雜於名稱中間,不必全部附綴於方法名稱的尾端,可以提高程序可讀性(感覺很不習慣( ⊙ o ⊙ )啊!)。設定顏色RGB值的方法爲例:

- (void) setColorToRed: (float)red Green: (float)green Blue:(float)blue; /* 宣告方法*/

[myColor setColorToRed: 1.0 Green: 0.8 Blue: 0.2]; /* 呼叫方法*/

這個方法的簽名是setColorToRed:Green:Blue:。每個冒號後面都帶着一個float類別的參數,分別代表紅,綠,藍三色。

Implementation

@Implementation用於定義類的實現,以@end結尾。

@Implementation內定義的變量是私有的。

創建對象

MyObject * my = [[MyObject alloc] init];

在Objective-C 2.0裏,若創建對象不需要參數,則可直接使用new

MyObject * my = [MyObject new];

alloc分配內存,init是初始化對象。

方法

方法聲明語法

類方法使用+

實例方法使用-

給myArray變量傳遞消息insertObject:atIndex:消息

[myArray insertObject:anObj atIndex:0];

一個嵌套的消息

[[myAppObject getArray] insertObject:[myAppObject getObjectToInsert] atIndex:0];

下面的例子演示了一個類方法如何作爲類的工廠方法。在這裏,arrayWithCapacity是NSMutableArray類的類方法,爲類的新實例分配內容並初始化,然後返回給你。

NSMutableArray*   myArray = nil; // nil 基本上等同於 NULL

// 創建一個新的數組,並把它賦值給 myArray 變量
myArray = [NSMutableArray arrayWithCapacity:0];

屬性

屬性聲明應該放在類接口的方法聲明那裏。基本的定義使用@property編譯選項,緊跟着類型信息和屬性的名字。你還可以用定製選項對屬性進行配置,這決定了存取方法的行爲。下面的例子展示了一些簡單的屬性聲明:

@interface Person : NSObject {
    @public
        NSString *name;
    @private
        int age;
}

@property(copy) NSString *name;
@property(readonly) int age;

-(id)initWithAge:(int)age;
@end

屬性的訪問方法由@synthesize關鍵字來實現,它由屬性的聲明自動的產生一對訪問方法。另外,也可以選擇使用@dynamic關鍵字表明訪問方法會由程序員手工提供。

@implementation Person
@synthesize name;
@dynamic age;

-(id)initWithAge:(int)initAge
{
    age = initAge; // 注意:直接賦給成員變量,而非屬性
    return self;
}

-(int)age
{
    return 29; // 注意:並非返回真正的年齡
}
@end

屬性可以利用傳統的消息表達式、點表達式或"valueForKey:"/"setValue:forKey:"方法對來訪問。

Person *aPerson = [[Person alloc] initWithAge: 53];
aPerson.name = @"Steve"; // 注意:點表達式,等於[aPerson setName: @"Steve"];
NSLog(@"Access by message (%@), dot notation(%@), property name(%@) and direct instance variable access (%@)",
      [aPerson name], aPerson.name, [aPerson valueForKey:@"name"], aPerson->name);

爲了利用點表達式來訪問實例的屬性,需要使用"self"關鍵字:

-(void) introduceMyselfWithProperties:(BOOL)useGetter
{
    NSLog(@"Hi, my name is %@.", (useGetter ? self.name : name)); // NOTE: getter vs. ivar access
}

類或協議的屬性可以被動態的讀取。(不明白)

int i;
int propertyCount = 0;
objc_property_t *propertyList = class_copyPropertyList([aPerson class], &propertyCount);

for ( i=0; i < propertyCount; i++ ) {
    objc_property_t *thisProperty = propertyList + i;
    const char* propertyName = property_getName(*thisProperty);
    NSLog(@"Person has a property: '%s'", propertyName);
}

快速枚舉

比起利用NSEnumerator對象或在集合中依次枚舉,Objective-C 2.0提供了快速枚舉的語法。在Objective-C 2.0中,以下循環的功能是相等的,但性能特性不同。

// 使用NSEnumerator
NSEnumerator *enumerator = [thePeople objectEnumerator];
Person *p;

while ( (p = [enumerator nextObject]) != nil ) {
    NSLog(@"%@ is %i years old.", [p name], [p age]);
}
// 使用依次枚舉
for ( int i = 0; i < [thePeople count]; i++ ) {
    Person *p = [thePeople objectAtIndex:i];
    NSLog(@"%@ is %i years old.", [p name], [p age]);
}
// 使用快速枚舉
for (Person *p in thePeople) {
    NSLog(@"%@ is %i years old.", [p name], [p age]);
}

快速枚舉可以比標準枚舉產生更有效的代碼,由於枚舉所調用的方法被使用NSFastEnumeration協議提供的指針算術運算所代替了。

協議

正式協議類似於Java中的"接口",它是一系列方法的列表,任何類都可以聲明自身實現了某個協議。在Objective-C 2.0之前,一個類必須實現它聲明匹配的協議中的所有方法,否則編譯器會報告錯誤,表明這個類沒有實現它聲明匹配的協議中的全部方法。Objective-C 2.0版本允許標記協議中某些方法爲可選的(Optional),這樣編譯器就不會強制實現這些可選的方法。

協議經常應用於Cocoa中的委託及事件觸發。例如文本框類通常會包括一個委託(delegate)對象,該對象可以實現一個協議,該協議中可能包含一個實現文字輸入的自動完成方法。若這個委託對象實現了這個方法,那麼文本框類就會在適當的時候觸發自動完成事件,並調用這個方法用於自動完成功能。

Objective-C中協議的概念與Java中接口的概念並不完全相同,即一個類可以在不聲明它匹配某個協議的情況下,實現這個協議所包含的方法,也即實質上匹配這個協議,而這種差別對外部代碼而言是不可見的。正式協議的聲明不提供實現,它只是簡單地表明匹配該協議的類實現了該協議的方法,保證調用端可以安全調用方法。

語法

協議以關鍵字@protocol作爲區塊起始,@end結束,中間爲方法列表。

@protocol Locking
- (void)lock;
- (void)unlock;
@end

這是一個協議的例子,多線程編程中經常要確保一份共享資源同時只有一個線程可以使用,會在使用前給該資源掛上鎖 ,以上即爲一個表明有"鎖"的概念的協議,協議中有兩個方法,只有名稱但尚未實現。

下面的SomeClass宣稱他採納了Locking協議:

@interface SomeClass : SomeSuperClass <Locking>
@end

一旦SomeClass表明他採納了Locking協議,SomeClass就有義務實現Locking協議中的兩個方法。

@implementation SomeClass
- (void)lock {
  // 實現lock方法...
}
- (void)unlock {
  // 實現unlock方法...
}
@end

由於SomeClass已經確實遵從了Locking協議,故調用端可以安全的發送lock或unlock消息給SomeClass實體變量,不需擔心他沒有辦法迴應消息。

動態類型

Objective-C具備動態類型:即消息可以發送給任何對象實體,無論該對象實體的公開接口中有沒有對應的方法。對比於C++這種靜態類型的語言,編譯器會擋下對(void*)指針調用方法的行爲。但在Objective-C中,你可以對id發送任何消息(id很像void*,但是被嚴格限制只能使用在對象上),編譯器僅會發出"該對象可能無法迴應消息"的警告,程序可以通過編譯,而實際發生的事則取決於運行期該對象的真正形態,若該對象的確可以迴應消息,則依舊運行對應的方法。

一個對象收到消息之後,他有三種處理消息的可能手段,第一是迴應該消息並運行方法,若無法迴應,則可以轉發消息給其他對象,若以上兩者均無,就要處理無法迴應而拋出的例外。只要進行三者之其一,該消息就算完成任務而被丟棄。若對"nil"(空對象指針)發送消息,該消息通常會被忽略,取決於編譯器選項可能會拋出例外。

雖然Objective-C具備動態類型的能力,但編譯期的靜態類型檢查依舊可以應用到變量上。以下三種聲明在運行時效力是完全相同的,但是三種聲明提供了一個比一個更明顯的類型信息,附加的類型信息讓編譯器在編譯時可以檢查變量類型,並對類型不符的變量提出警告。

下面三個方法,差異僅在於參數的形態:

- setMyValue:(id) foo;

id形態表示參數"foo"可以是任何類的實例。

- setMyValue:(id <aProtocol>) foo;

id<aProtocol>表示"foo"可以是任何類的實例,但必須採納"aProtocol"協議。

- setMyValue:(NSNumber*) foo;

該聲明表示"foo"必須是"NSNumber"的實例。

轉發

Objective-C允許對一個對象發送消息,不管它是否能夠響應之。除了響應或丟棄消息以外,對象也可以將消息轉發到可以響應該消息的對象。轉發可以用於簡化特定的設計模式,例如觀察者模式或代理模式。

Objective-C運行時在Object中定義了一對方法:

轉發方法

- (retval_t) forward:(SEL) sel :(arglist_t) args; // with GCC
- (id) forward:(SEL) sel :(marg_list) args; // with NeXT/Apple systems

響應方法

- (retval_t) performv:(SEL) sel :(arglist_t) args;  // with GCC
- (id) performv:(SEL) sel :(marg_list) args; // with NeXT/Apple systems

類別(Category)

 

文章參考

https://www.runoob.com/w3cnote/objective-c-tutorial.html

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章