Objective-C 快速參考

Objective-C 快速參考

Objective-C是利用幾個重要的觀念和C一些語法構成,然後再配合Foundation Framework完成最基本的Objective-C的環境在iOS或OSX上執行的APP,所以在文中只針對Objective-C提供的特性去做說明,Foundation Framework就歸類於Framework再做介紹。

呼叫方法 (Calling Methods)

基本方法

Objective-C在呼叫方法時是以訊息的方式傳遞,而方法如果有參數時就必需要以:冒號隔開。

//-----------start-----------
[object method];
[object methodWithParameter:parameter];
//------------end------------

有回傳值的方法

方法的結果可以有回傳值,但回傳值只接受單一的。

//-----------start-----------
returnValue=[object method];
returnValue=[object methodWithParameter:parameter];
//------------end------------

多參數輸入

在方法有很多參數必需傳入時,每個參數都會有個名稱,名時後同樣用:隔開傳入的參數值,參數與參數之間用空白做區隔,這是與一般語言較不一樣的地方。

//-----------start-----------
[object methodWithParameter:parameter Parameter1:parameter1];
//------------end------------

所以在傳入的參數數量不同情況下也可以使用相同名稱的方法。

//-----------start-----------
[object methodWithParameter:parameter Parameter1:parameter1 Parameter1:parameter2];
//------------end------------

編譯器會自動根據傳入參數數量自動找到符合的的方法。

呼叫自已物件中的方法:

一個物件也具備方法呼叫自已物件的方法,在呼叫自已物件中的法時需要使用前綴字self來呼叫。

//-----------start-----------
    [self method];
//------------end------------

舉個實際例子:

//-----------start-----------
- (void)method {
    [self method2:@"method1"];
}
//
- (void)method2:(NSString *)parameter {
    NSLog(@"%@", parameter);
}
@end
//------------end------------

巢狀輸入

巢狀輸入的方法式意思就是說,一個物件呼叫方法後得到的結果直接傳入另一個物件的方法之中,這種方式在高階語言都會提供。

//-----------start-----------
[object2 methodWithParameter:[object1 methodWithParameter:parameter]];
//------------end------------

宣告物件

宣告物件在Objective-C的慣利是需要先宣告記憶體配置之後再初始化物件,之後整個物件才能開始使用它的方法。

基本建立物件

宣告物件的記憶體配置使用方法alloc

//-----------start-----------
[object alloc];
//------------end------------

初始化物件最基本的是使用init,所以當你在自訂的物件時,本身也要去作init這個方法。

//-----------start-----------
[object init];
//------------end------------

後來都是使用巢狀輸入的方式直接建立新的物件

//-----------start-----------
[[object alloc] init];
//------------end------------

之後Objective-C又提供更便民的方式,就是使用new方法。

//-----------start-----------
[object new]; //等於alloc + init
//------------end------------

帶有初始值的建立物件

有些物件會提供initWith的前綴字開頭的提供初始化後直接指定某個值為預設值,所以建立物件需要順帶建立初始值時,使用這個方法相當的便利,自行實作時也可以參照這樣規則的前綴字來建立方法。

//-----------start-----------
[object initWithTopValue:123]; //初始化後會指定Top值為123
//------------end------------

多參數時也可以:

//-----------start-----------
[object initWithTopValue:123 Parameter1:parameter1]; //初始化後會指定Top值為123
//------------end------------

建立物件

建立物件時,Objective-C與C語言一樣,會有個標頭檔.h與程式檔.m,標頭檔記載著物件的介面與方法的名稱及需要引入其他使用物件的標頭檔,程式檔則記載著物件方法的程式行為以及變數的宣告。

介面實作 (Interface)

介面實作(interface)的內容是存在標頭檔.h之中,下面為最基本的介面

//-----------start-----------
// Test1.h
#import <UIKit/UIKit.h>

@interface Test1 : NSObject {
    NSString* text1;
}
@end
//------------end------------

增加方法的宣告

//-----------start-----------
// Test1.h
#import <UIKit/UIKit.h>

@interface Test1 : NSObject {
    NSString* text1;
}

- method;

@end

//------------end------------

實作區段 (Implementation)

實作區段(implementation)的內容是存在程式檔.m中,之前已經實作介面,這裡要配合介面來寫程式內容,因有增加方法method,所以將功能寫上。

//-----------start-----------
// Test1.m
#import "Test1.h"

@implementation Test1

- (NSString*) method {
    return text1; //將text1內容回傳
}

@end

//------------end------------

自動產生存取變數 (Property)

Objective-C中,如果要存取物件中的變數必需要實作寫(setter)、讀(getter)的方法,無法像某些語言可以直接存取,但一般來說每次都要自行實作實在很麻煩,所以Objective-C提供了Property這個方試來自動建立,但自動建立時會依照它內定的方式來命名,所以必需要配合Synthesize自動建立符合自已命名的變數名稱。

上述是使用method方法來取得text1的內容,下面在標頭檔中使用 Property 直接自動產生存取方式。

//-----------start-----------
// Test1.h
#import <UIKit/UIKit.h>

@interface Test1 : NSObject {
    NSString* text1;
}
@property (nonatomic,strong) NSString* text1;

@end
//------------end------------

先看Property宣告前必需要知道的屬性:

@property (屬性1, 屬性2) type propertyName;

屬性可以填入的如下做個簡單的解釋:

  • strong 使物件保持存在不會失去參考計數
  • weak 物件失去參考時會設置成nil
  • assign 一般的指定,無參考計數

strong、assign為ARC開啟時才會被要求使用

  • copy Make copy on assign 直接拷貝一個新的,參考計數為1,但釋放原來的參考計數(減1)
  • nonatomic Make not threadsafe, increase perf 建立一個不在執行緒中使用 *readwrite 可以讀取與寫入,允許建立getter與setter
  • readonly 只能讀取,只能建立getter

例子中使用(nonatomic,strong),一般ARC宣告都使用這類的宣告。我們所宣告的text1因為它部分程式內容是自動產生的,依照編譯器的命名習慣,它會在變數名稱前面加上_,所以我們要存取時就必需使用_text1名稱,例子:

//-----------start-----------
_text11 = @"1234" //重新給與新的值

NSLog ("%@",_text1); //取得值並將內容印出
//------------end------------

在使用上如果很不方便的話,可以在程式檔中加上合成的方式讓編譯器自動合成為text1名稱方法來使用。

//-----------start-----------
// Test1.m
#import "Test1.h"

@implementation Test1

@synthesize text1;

@end
//------------end------------

完成後就可以使用text1來存取變數。

//-----------start-----------
text11 = @"1234" //重新給與新的值
NSLog ("%@",text1); //取得值並將內容印出
//------------end------------

或是可以用現在最常用的存取來使用,假設aTest1被宣告成Test1物件類型,使用的方式就可以像下面這樣:

//-----------start-----------
aTest1.text11 = @"1234" //重新給與新的值
NSLog ("%@",aTest1.text1); //取得值並將內容印出
//------------end------------

早期沒有這個方式可能就要自行建立setter/getter的方法,使用時的規則如:

//-----------start-----------
// Test1.h
#import <UIKit/UIKit.h>

@interface Test1 : NSObject {

}

-(void) setText1: (NSString*) d
-(NSString*) text1
-
@end
//------------end------------

//-----------start-----------
// Test1.m
#import "Test1.h"

@implementation Test1 {
    NSString* text1;
}

-(void) setText1: (NSString*) d
{
    text1 = d;
}

-(NSString*) text1
{
    return text1;
}
@end
//------------end------------

//-----------start-----------
[aTest1 setText1:@"1234"] //重新給與新的值
NSLog ("%@",[aTest1 text1]); //取得值並將內容印出
//------------end------------

原有物件直接增加方法 (Category)

Objective-C提供一個不需要用繼承的方式可以在原有物件上增加方法的功能,這種感覺很像替原有的物件增加功能,當你的專案需要對某個物件只是增加功能,不想要用繼承的方式去改變原本使用物件的名稱以及重新實作某些方法就可以使用。

一樣,都是有標頭檔與程式檔,只差在檔名有命名規則,實際名稱為CNSString,在Xcode中會以你要在要增加的物件名稱當做前綴字(Prefix),這個例子就如下面的:

//-----------start-----------
//NSString+CNSString.h
#import <Foundation/Foundation.h>

@interface NSString (CNSString)

-(void)printToNSLog;

@end
//------------end------------

要在NSString增加方法,所以檔名會是NSString+CNSString

//-----------start-----------
//NSString+CNSString.m
#import "NSString+CNSString.h"

@implementation NSString (CNSString)

-(void)printToNSLog
{
    NSLog(@"%@",[self description]);
}

@end

//------------end------------

使用上面只要將標頭檔引入(NSString+CNSString.h),再像一般的方式宣告一個NSString,就能使用新增的方法printToNSLog

//-----------start-----------
//ViewController.h
#import <UIKit/UIKit.h>
#import "NSString+CNSString.h"

@interface ViewController : UIViewController


@end
//------------end------------

//-----------start-----------
//ViewController.m
#import "ViewController.h"

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSString *n1=@"1234";
    [n1 printToNSLog]; //直接使用並將內容印至Log視窗
}

@end

//------------end------------

建立協議 (Protocol)

Objective-C protocol的方式,與Java提供的Interface類似,當你要宣告時,它必需要在@interface之前,通常架構像這樣:

//-----------start-----------
//ReadWriteValue.h
@protocol ReadWriteValue
:
-(void)didReadValue:(int)value;
-(void)didWroteValue;
:
@end
//------------end------------

物件使用protocol時需要用<名稱>去告知使用protocol,當然也要記得引入標頭檔,再利用protocol宣告的協議,程式需要時執行協議的命令。

//-----------start-----------
//Temperature.h
#import "ReadWriteValue.h"
@interface Temperature:NSObject <ReadWriteValue>
{
    id thisObject;
}
-(void)setDelegate:(id)delegate;
-(void)toReadValue;
-(void)toWroteValue:(int)value
:
:
:
@end

//Temperature.m
#import "Temperature.h"
@implementation Templature

-(void)setDelegate:(id)delegate
{
    thisObject = delegate;
}

-(void)toReadValue
{
if ([thisObject respondsToSelector: @selector (didReadValue:value)] == YES)
[thisObject didReadValue];

}

-(void)toWroteValue:(int)value
{
if ([thisObject respondsToSelector: @selector (didWroteValue)] == YES)
[thisObject didWroteValue];

}

:
@end
//------------end------------

以上就是產生protocol的方法,但在使用的物件裡有協議時,宣告好物件後,就必需要在implementation中實作protocol所要求的協議,並將它實作出來,下面例子利用Temperature物件來完成讀取溫度與寫入設定功能,並建立TaiwanTemperature物件來取得台灣的溫度,當溫度讀取完成時就會呼叫協議中的-(void)didReadValue:(int)value,告知程式讀取溫度已完成並將溫度值傳入協議的方法,所以只要實作它就可以取得溫度的值。

//-----------start-----------
//TaiwanTemperature.h
@interface TaiwanTemperature:NSObject <ReadWriteValue>
{

}
@end


//TaiwanTemperature.m
#import "TaiwanTemperature.h"
@implementation TaiwanTemperature
{

}

-(void)viewDidLoad
{
    Temperature *temperature=[[Temperature alloc] init];
    [temperature toReadValue];
    [temperature toWroteValue:1];
}

-(void)didReadValue:(int)value
{
    NSLog ("%i",value);//印出溫度值
}

-(void)didWroteValue;
{
    NSLog ("is wrote");//完成寫入設定時會呼叫的協議方法
}

@end


//------------end------------

 私有與公開宣告

私有與公開變數其中的差別在於宣告時放置的位子,如果在.h中宣告的內容為對外公開的,在其他程式要引用物件宣告的內容時可以使用它,反之如果在.m中宣告則不管物件封裝成Library或是其他,它都會與程式在非公開的範圍,除非改於.h標頭檔中提供。

// Test1.h
#import <UIKit/UIKit.h>

@interface Test1 : NSObject {
// 定義公開的變數
    NSString *string1;
}
// 定義公開的 properties
// 定義公開的 methods
@property (nonatomic,strong) NSString* text1;
-(void) test1
//
- method;

@end

// Test1.m
#import "Test1.h"

@implementation Test1 {
    NSString *string2;
// 定義私有的常例變數
}
:
:

@end

提醒

整篇文章內容以ARC為主,看完後可以知道Objective-C語言的基本架構。在這環境中許多的語法其實與C很相近,所以只需要先理解基本架構就能開始使用Objective-C的環境,然而官方也提供ProgrammingWithObjectiveC 電子書,裡面有此篇未介紹的基本語法及範例供參考,之後也會陸續的直接介紹語法的內容。(2014.04.20)