2017年1月5日 星期四

Ch08 - 介紹 Objective-C 類別(2)

Objective-C 程式設計學習筆記 Ch08

  • Ch08-介紹 Objective-C 的類別相關的應用。
這章節只是更深入介紹一些關於類別的功能,像是...上一章節,我們已經學會將介面檔 (interface)、實作檔(implementation) 分開,並且學會實作 getter 以及 setter 方法,這章節我們會學到使用 @property 和 @synthesize 來實做 setter 和 getter 兩個方法。
另外我們會深入地使用一些方法,像是實作傳遞多個參數到一個方法,以及介紹兩個關鍵字的使用方式 static 以及 self 關鍵字。

目錄:

  • (1) @property 以及 @synthesize
  • (2) static 關鍵字
  • (3) self 關鍵字

(1) @property 以及 @synthesize

上一章節 Mobile 的類別,我們是自己寫 getCharge 以及 setCharge 方法這一組 getter 與 setter,分別是取得 chargeValue 以及設定 chargeValue。
現在只要使用 Objective-C 的 @property 以及 @synthesize 就可以幫我們達成這件事情,@property 寫在 interface 介面檔(ex: mobile.h),定義需要產生 getter 與 setter 的方法(注意,@property並不是寫在 @interface 宣告區塊裡,而是獨立的一個區塊),而 @synthesize 是寫在 implementation實作檔裡(ex: mobile.m),為指定的實體變數產生一組 gettet 與 setter 方法。

@property 的宣告方式:

使用 @property 時,就不需要在 interface 區段宣告一樣的實體變數了。比方說 interface 已經有宣告 int charge,而 @property int charge; 也已經宣告過一次了,因此可以不用重複宣告 charge (雖然有宣告也不會有什麼編譯上的錯誤)。
@property (型別) 實體變數或屬性
我們來改寫一下 mobile.h,你就會知道 @property 在 interface 介面檔如何使用: 這是目前的 Mobile.h:
因為註解有點多,我們把註解拿掉,以下是目前比較完整以及乾淨的 Mobile.h:
#import <Foundation/Foundation.h>

@interface Mobile : NSObject

@property int charge;
@property bool mobileStatus;

-(void) setShutdown;
-(void) print;

@end
  • 使用 @property 指令識別屬性 charge 以及 mobileStatus。
  • 而實體方法有 setShutdown 以及 Print。

@synthesize 的宣告方式:

@synthesize 其實也沒什麼特別的宣告方式,跟 @property 很像,只是 @synthesize 是寫在實作檔。
還有不要忘記為什麼要用 @synthesize,是因為 @synthesize 可以自動幫我們生成 getter 與 setter 方法,而且需要搭配 interface 介面檔中的 @property。但是在 Xcode 4.5 之後已經可以不用 @synthesize 了,只要使用 @property 就可以幫你產生 getter 與 setter,即便如此,我們的範例還是會使用 @property 搭配 @synthesize 來示範。
@synthesize 後面只需要接實體變數或是屬性就可以了:
@synthesize  屬性或實體變數名稱;
當你有多個實體變數或是屬性需要 @synthesize 時,也可以寫在同一行,用『,』隔開:
@synthesize 屬性或實體變數名稱, 屬性或實體變數名稱;
回到我們的 mobile.m 檔,接著我們要讓 setCharge, getCharge 方法以及讓 mobileStatus 使用 @synthesize 的方法產生 getter 與 setter,我們更動了一下 mobile.m 檔,已經被註解的地方表示已經不需要自己宣告的部分:
現在畫面有點亂,下面是已經整理過後的 mobile.m 檔:
#import "Mobile.h"

@implementation Mobile

@synthesize charge;
@synthesize mobileStatus;

-(void) setShutdown{
    mobileStatus = 1;
}
-(void) print{
    NSLog(@"Now your charge value is %i", charge);

    if(mobileStatus==1){
        NSLog(@"Now your mobile shutdown");
    }else{
        NSLog(@"Now your mobile is on standby");
    }
}

@end

大功告成

方便的事情是,現在我們已經利用 @property 以及 @synthesize 幫我們完成自動產生 getter 以及 setter。可是,怎麼用呢? 當我們用 @synthesize 製作 charge 的 setter 與 getter 時:
  • getter 方法使用方式就是直接輸入方法名稱,如 [win_mobile charge]。
  • setter 方法使用方式就是 set+ charge,也就是 setCharge,如[win_mobile setCharge:85]。
getCharge 跟 setCharge 就是上一章我們自己寫的 getter/setter,透過 @synthesize,我們可以省去自己寫這段 code。
執行你的 main.m
你會發現當我們改寫 mobile.h 以及 mobile.m 檔,改為使用 @synthesize 的方式,完全不需要動到 main.m,若你現在再次執行 main.m,一樣可以執行。
#import <Foundation/Foundation.h>
#import "Mobile.h"

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        Mobile *win_mobile =[[Mobile alloc] init];
        [win_mobile setCharge:85];
        [win_mobile setShutdown];
        [win_mobile print];
    }
    return 0;
}
你可以自己玩玩看有哪些變化跟差別: 在下面這個範例中,先是用 setCharge 改變 charge 的值為 40,然後 NSLog 使用 getter 取得 charge 的值。
#import <Foundation/Foundation.h>
#import "Mobile.h"

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        Mobile *win_mobile =[[Mobile alloc] init];
        [win_mobile setCharge:40];
        NSLog(@"charge value is %i", [win_mobile charge]);
    }
    return 0;
}

(2) static 關鍵字

若要講 static 關鍵字的用法,必須要回溯到之前講到的區域變數的概念,還記得區域變數只有在定義他們的方法中可以使用嗎?那麼如果你希望有一個區域變數也可以被其他的方法呼叫且改變其值,那該怎麼辦? 使用 static 關鍵字來宣告那個變數,就可以讓該變數仍然保有他的值。
如果你將某個變數宣告前面的修飾詞加上 static,那該變數就會成為靜態變數,例如宣告一個靜態變數 dountsNumber,初始值為 0 (使用 static 修飾詞的變數有沒有設定初始值都沒差,因為預設都會為 0)。
(其實我不太習慣稱 static 為關鍵字,這是很多翻譯書的問題,其實其他程式語言的 class 也會有 static,只是有些書翻 static 為靜態成員或是靜態變數都有)

我們來看一個簡單的例子,寫一個簡單的 Test Class :

這個類別只有一個方法 count,count 內部實作有一個 static 靜態成員 number,預設值為 0,只要呼叫(傳遞) count 這個方法給實體,就會將 number 的累加 1。
@interface Test : NSObject
-(int) count;
@end


@implementation Test
-(int) count{
    static int number;
    number++;
    return number;
}
@end

程式部分:

程式的部分非常單純,建立一個 firstTime 實體,NSLog 的值為呼叫 count 方法,取得 count 方法的回傳值:
Test *firstTime = [[Test alloc] init];
NSLog(@"now number is %i", [firstTime count]);

Test *secondTime = [[Test alloc] init];
NSLog(@"now number is %i", [secondTime count]);

執行結果:

2014-01-16 23:14:35.039 ch07[1481:303] now number is 1
2014-01-16 23:14:35.040 ch07[1481:303] now number is 2
你會發現 firstTime 以及 secondTime 一樣都是呼叫 count 的值,但 secondTime 所回傳的 number 的值卻是累加後的結果,這就是 static 關鍵字的用處,讓該區域變數被呼叫時能然保有其值 。

[燈泡]或許你把 static 關鍵字拿掉,就會知道結果的不同了,試試看!:

我們把 static int number 改成 int number=0,讓 number 變成非靜態成員;
@interface Test : NSObject
-(int) count;
@end


@implementation Test
-(int) count{
    int number=0;
    number++;
    return number;
}
@end
程式的部分如下:
Test *firstTime = [[Test alloc] init];
NSLog(@"now number is %i", [firstTime count]);
NSLog(@"now number is %i", [firstTime count]);

Test *secondTime = [[Test alloc] init];
NSLog(@"now number is %i", [secondTime count]);
執行結果: 執行結果會發現,count 方法每次都是從初始值 0 開始累加 1,可是沒有保持其值,因為對於每次呼叫 count 方法以及 number++ 時,初始值都是 0,即便呼叫兩次 [firstTime count] 也是一樣,沒有使用 static 關鍵字就沒有保持其值。
2014-01-16 23:26:36.459 ch07[1552:303] now number is 1
2014-01-16 23:26:36.461 ch07[1552:303] now number is 1
2014-01-16 23:26:36.462 ch07[1552:303] now number is 1

改變 static 靜態變數的存取區域

承最原始的範例,既然我們是把 static 靜態區域變數宣告在 @implementation 區域的 count 方法裡面,因此只有利用實體的 count 方法才能存取 static 靜態變數,靜態成員 number 在這個範例中只是區域變數,如果你想要讓 static 靜態成員不夠透過實體方法也能改變其值,就必須將 static 靜態變數拉出方法的 @implementation 定義之外。

範例 (注意 static int number=0 的程式位置):

  • 類別宣告部分:
@interface Test : NSObject
-(int) count;
@end

static int number=0;
@implementation Test
-(int) count{
    number++;
    return number;
}
@end
  • 程式部分:
Test *firstTime = [[Test alloc] init];

//傳遞方法給實體
NSLog(@"now number is %i", [firstTime count]);
NSLog(@"now number is %i", [firstTime count]);
NSLog(@"now number is %i", [firstTime count]);
NSLog(@"now number is %i", [firstTime count]);

//直接取得 number 的值
NSLog(@"result Number is %i", number);

//也能直接改變 number 的值
number = 10;
NSLog(@"result Number is %i", number);
  • 執行結果:
2014-01-16 23:44:05.051 ch07[1661:303] now number is 1
2014-01-16 23:44:05.053 ch07[1661:303] now number is 2
2014-01-16 23:44:05.053 ch07[1661:303] now number is 3
2014-01-16 23:44:05.054 ch07[1661:303] now number is 4
2014-01-16 23:44:05.054 ch07[1661:303] result Number is 4
2014-01-16 23:44:05.054 ch07[1661:303] result Number is 10

(3) self 關鍵字

既然講了 static 那就一起講個 self 關鍵字好了。其實大部分具有物件導向特質的程式語言,都有自己呼叫類別的方法或是呼叫父類別的方法。
而 self 關鍵字的 self 代表自己,嚴格來說應該是代表著當前方法的調用/呼叫者。self 用在一個方法裡面要呼叫另外一個方法。

舉例: 這是一個 Demo 的 class

setX: andY: 方法用來相加所帶入的兩個參數 (x,y),接著 setX: andY: 還會再自己呼叫 getResult 的方法([self getResult]),並且 return 相加的結果。
@interface Demo : NSObject
-(int) setX:(int) x andY:(int) y;
-(int) getResult;
@end


@implementation Demo
{
    int xvalue,yvalue;
}
-(int) getResult{
    return xvalue + yvalue;
}

-(int) setX:(int) x andY:(int) y{
    xvalue = x;
    yvalue = y;
    return [self getResult];
}

@end
程式部分:
Demo *testAdd = [[Demo alloc] init];
NSLog(@"testAdd 10 + 18 = %i", [testAdd setX:10 andY:18]);
執行結果:
2014-01-17 21:30:44.296 ch07[6254:303] testAdd 10 + 18 = 28
另一個關鍵字為 super,代表呼叫父類別的方法,我們會在下一章節『繼承』介紹。

下一章節,將會介紹 Objective-C 繼承 。

0 意見:

張貼留言