2013年5月29日 星期三

Categories -- 修改現有的類別

使用時機:

想為已經存在的類別新增或修改方法(不建議修改原本的方法,因為繼承他的子類別也會受影響),你或許會問,那為什麼不在原本的.h .m檔中新增就好,幹嘛這麼麻煩使用categories,因為你有可能拿不到原本source code或是沒有權限修改(例如 NSString),而且如果大家都在同一份中作修改,那會改的亂七八糟的,這時候依功能把它獨立出來,個人寫個人的,就方便許多。


Dog.h
#import <Foundation/Foundation.h>

@interface Dog : NSObject
{
    NSString *_name;
}
-(id)initWithName:(NSString *)name;
-(void)play;

@end

Dog.m
#import "Dog.h"

@implementation Dog

-(id)initWithName:(NSString *)name
{
    self = [super init];
    if (self) {
        _name = name;
    }
    return self;
}

-(void)play
{
    NSLog(@"%@ is playing.",_name);
}

@end

這隻狗只會玩,幫他添加點聲音,新增Category檔案
選擇Category on “Dog”
Dog+Bark.h
#import "Dog.h"

@interface Dog (Bark)

-(void)barkWaWa;
-(void)barkMeMe;

@end

Dog+Bark.m
#import "Dog+Bark.h"

@implementation Dog (Bark)

-(void)barkWaWa
{
    NSLog(@"%@ WaWa !!",_name);
}
-(void)barkMeMe
{
    NSLog(@"%@ MeMe !!",_name);
}


@end

從檔名可以看出來Category是附加在原本的類別上的,我們把關於叫聲的方法都放在Bark這個Category,要注意的是 Dog (Bark)的寫法,是用小刮號表示,另外在Category無法新增var,只能增加方法(可以宣告不一定每個都實作出來),如果要add instance variable,請用extensions。

main.m
#import <Foundation/Foundation.h>
#import "Dog.h"
#import "Dog+Bark.h"

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

    @autoreleasepool {
        
        Dog *d = [[Dog alloc]initWithName:@"Tom"];
        [d play];
        [d barkWaWa];
        [d barkMeMe];
    }
    return 0;
}

現在它除了會玩之外還會叫了,特別要注意的是,所有繼承於Dog類別的子類別也都學會叫囉。


Informal Protocols

category 有時也稱作 informal protocol,常常我們在 root class 時,會一併宣告 category,此時並不實作出這些方法(category.m 沒有 implement),而是等到子類別時,才選擇要實現哪些方法,在子類別的 .h中重新宣告,.m 中實作。那可能有人會問,這麼麻煩幹麼,都要在子類別重新宣告了,category 還有用的必要嗎,其實這時候 category 有點像是把這些方法先文件化、模組化,統一放在一個根類別加以說明,而不要四散在各處,這讓以後的人比較容易了解此類別的用途。

Private Method

在Objective-C裡頭,方法的修飾不像 instance有@public, @private, @protected,宣告時放在.h 的是public, 放在 .m的是private,That's all,那這個跟Category有什麼關係,原來是放在.m的寫法需要用到,category的名稱是空白的,匿名Category,其實這就是 Class Extension

@interface Dog()

 //private method
-(void) privateMethod;

@end

2013年5月28日 星期二

Hello, Objective-C


怎麼下載與安裝Xcode已經不是問題,開啓一個新的專案,選擇Command Line Tool作為Hello world 的範本。

Product Name, Organization Name, Company identifier 隨便取,Xcode會結合company identifier and product name 成為識別這個專案的bundle identifier,記得勾選 Use Automatic Reference Counting,幫我們自動管理記憶體。 


Project裡面有個main.m檔,其中有個main function,這是專案在執行時的入口,通常在開發app時不太會動到這個檔案。已經幫你寫好Hello, World了,按下左上角的Run,在下方的訊息窗口,即跑出Hello, World!


// 表示注解,在它右邊的文字,編譯器會忽略,如果有多行注解可以用
/*
我是注解第一行
我是注解第二行
......
......
*/

註解要寫得清楚,最好是能即時就寫下來,不要想說以後會補,因為通常就是不會補了。

#import表示載入這個檔案,好讓你可以使用定義在其中的一些成員或函式,使用<>刮起來的是系統標頭檔,如果是自己寫的,用雙引號" "刮起來。而Fundation.h這個標頭檔包含了一堆標頭檔,你可以反白按下右鍵點選jump to definition,就可以看到他的內容。


而在@autoreleasepool {  } 括弧中的變數與物件,會自動釋放記憶體,不用自己去操作。
NSLog是一個函式,可以在寫程式的過程中,印出一些資訊,讓你可以追蹤與除錯,而它接受一個NSString物件,在Objective-C中,以@""表示。

return 0 表示程式結束正常,任何非零表示有問題發生。


讓我們來修改一下,看看有什麼變化。
int main(int argc, const char * argv[])
{

    @autoreleasepool {
        
        NSLog(@"第一行\n");
        NSLog(@"第二行\n");
        NSLog(@"\"結束\"");
        
    }
    return 0;
}



\n是換行字元,如果要在輸出中表示雙引號,再多加個反斜線。

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

    @autoreleasepool {
        
        int sum;
        
        sum = 7 + 11;
        NSLog(@"7 + 11 = %i ",sum);
        
    }
    return 0;
}


%i     表示一個整數,常用的還有
%@   objective-C 物件
%f     浮點數
..........
..........

更多地表示符號參考

Objective-C 學習筆記


我想很多人會學習Objective-C都是因為要開發手機應用程式,當然我也不例外,上圖引用Tiobe網站目前各程式語言熱門的程度,可以清楚地看出2009年以後,學習Objective-C的人大爆發,今年是2013年,不知道這股浪潮可以持續到什麼時候,不過未來自有未來的煩惱,現在想太多也沒有用,只希望此刻可以快樂地學習。以下的文章是我閱讀 Stephen G. Kochan   Programming in Objective-C 2.0 的讀書筆記,藉著自己不佳的英文程度去理解文章,如果有誤,非常感謝您的指正。

入門
           for 迴圈

框架(Framework)


2013年5月1日 星期三

objective-c function 根據參數類型,有不同的行為

在objective-c 函式中,如果傳入的是簡單型別或是常數,函式會自動複製一份傳入的參數,所以之後如果有更改原先的變數,物件內的成員不會受影響;反之如果傳入的不是簡單型別或是常數,函式只會將物件的成員指向同一個位置,如果有更改變數,物件內的成員也跟著變動,這樣解釋有點模糊,看個例子比較清楚。
//XYPoint為自訂的一個類別
@interface XYPoint : NSObject
{
    int x;
    int y;
}
@property int x, y;

-(void)setX:(int) xVal andY:(int) yVal;

@end

@implementation XYPoint

@synthesize x,y;

-(void)setX:(int)xVal andY:(int)yVal
{
    x = xVal;
    y = yVal;
}

@end


//主要的測試類別,成員包含自定類別XYPoint, NSMutableString, int
@interface Foo : NSObject
{
    XYPoint *o;
    NSMutableString *name;
    int X;
}
-(XYPoint *)o;
-(void)setO:(XYPoint *)pt;
-(NSMutableString *)name;
-(void)setName:(NSMutableString *)n;
-(int)X;
-(void)setX:(int)value;
@end

@implementation Foo

-(XYPoint *)o
{
    return o;
}
-(void)setO:(XYPoint *)pt
{
    o = pt;
}

-(NSMutableString *)name
{
    return name;
}
-(void)setName:(NSMutableString *)n
{
    name = n;
}
-(int)X
{
    return X;
}
-(void)setX:(int)value
{
    X = value;
}
@end


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

    @autoreleasepool {
        
        //先準備等一會要傳入Foo的變數
        XYPoint *myPoint = [[XYPoint alloc]init];
        NSMutableString *myName = [[NSMutableString alloc]initWithString:@"Jon"];
        [myPoint setX:100 andY:200];
        int myValue = 10;
        
        //建立一個Foo物件,將成員設定為上述的變數
        Foo *f = [[Foo alloc]init];
        f.o = myPoint;
        [f setName:myName];
        [f setX:myValue];

        NSLog(@"Before change myPoint, f.o.x : %d f.o.y : %d",f.o.x,f.o.y);
        NSLog(@"Before change myName, f.name : %@",f.name);
        NSLog(@"Before change myValue, f.X : %d",f.X);
        
        //更改這些變數,看看是否為影響f內的成員
        [myPoint setX:50 andY:50];
        [myName setString:@"Tom"];
        myValue = 5;

        NSLog(@"After change myPoint, f.o.x : %d f.o.y : %d",f.o.x,f.o.y);
        NSLog(@"After change myName, f.name : %@",f.name);
        NSLog(@"After change myValue, f.X : %d",f.X);
    }
    return 0;
}


結果如下:
Before change myPoint, f.o.x : 100 f.o.y : 200
Before change myName, f.name : Jon
Before change myValue, f.X : 10
After change myPoint, f.o.x : 50 f.o.y : 50
After change myName, f.name : Tom
After change myValue, f.X : 10

2013年4月28日 星期日

Class variable in objective-c

在objective-c中沒有提到class variable,不過可以用static variable實現。

Toy.h

#import <Foundation/Foundation.h>
@interface Toy : NSObject
{
    NSString *name_;
}
@property (nonatomic,retain)NSString *name;
+(int)currentToyNumber;
@end


Toy.m
#import "Toy.h"

static int toyNumber;

@implementation Toy
@synthesize name = name_;
-(id)init
{
    self =[super init];
    if (self) {
        toyNumber++;
    }
    return self;
}
+(int)currentToyNumber
{
    return toyNumber;
}
-(void)dealloc
{
    toyNumber--;
}
@end


main.m
#import <Foundation/Foundation.h>
#import "Toy.h"
int main(int argc, const char * argv[])
{

    @autoreleasepool {
        //新增一個Toy
        Toy *toyA = [[Toy alloc]init];
        [toyA setName:@"Apple"];
        NSLog(@"Add toy, currentToyNumber: %d",[Toy currentToyNumber]);
        //再新增一個Toy
        Toy *toyB = [[Toy alloc]init];
        [toyB setName:@"Banana"];
        NSLog(@"Add another toy, currentToyNumber: %d",[Toy currentToyNumber]);
        //刪除一個Toy
        toyA = nil;
        NSLog(@"Delete apple, currentToyNumber: %d",[Toy currentToyNumber]);
        
    }
    return 0;
}

Output的結果如下:

Add toy, currentToyNumber: 1
Add another toy, currentToyNumber: 2
Delete apple, currentToyNumber: 1



用extern改寫,範例如下,其實是用全域變數,已經跟class variable含意不同了。
Toy.h
#import <Foundation/Foundation.h>
extern int currentToyNumber;

@interface Toy : NSObject
{
    
}
@end


Toy.m
#import "Toy.h"

@implementation Toy
-(id)init
{
    self = [super init];
    if (self) {
        currentToyNumber++;
    }
    return self;
}
-(void)dealloc
{
    currentToyNumber--;
}
@end

main
#import <Foundation/Foundation.h>
#import "Toy.h"


int currentToyNumber = 0;

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

    @autoreleasepool {
        
        NSLog(@"CurrentToyNumber : %d",currentToyNumber);
        
        Toy *toyA = [[Toy alloc]init];
        NSLog(@"CurrentToyNumber : %d",currentToyNumber);
        
        Toy *toyB = [[Toy alloc]init];
        NSLog(@"CurrentToyNumber : %d",currentToyNumber);
        
        toyA = nil;
        
        NSLog(@"CurrentToyNumber : %d",currentToyNumber);
    }
    return 0;
}
輸出結果:
CurrentToyNumber : 0
CurrentToyNumber : 1
CurrentToyNumber : 2
CurrentToyNumber : 1

2013年3月26日 星期二

IBOutlet 到底要放在property 還是ivar

最常見的是把IBOutlet放在property
@interface fooViewController : UIViewController {
   UILabel *fooLabel;
}
@property (nonatomic,weak)IBOutlet UILabel *fooLabel;
@end

但也有人放在ivar
@interface fooViewController : UIViewController {
    IBOutlet UILabel *fooLabel;
}
@property (nonatomic,weak)UILabel *fooLabel;
@end

甚至兩個地方都放的
@interface fooViewController : UIViewController {
    IBOutlet UILabel *fooLabel;
}
@property (nonatomic,weak)IBOutlet UILabel *fooLabel;
@end

這些程式碼都不會出錯,不過我還是想知道到底放在哪裡最好,查到了這一篇講得很清楚。
放在property比較有彈性,且編譯的速度快些,官方文件也是推薦放在property,有興趣的人可以看看。

2011年8月25日 星期四

灰熊人 Grizzly man



如果將提姆崔德威爾所拍攝的影片,當成灰熊的生態紀錄片來看,那就同把「老人與海」看作捕魚紀事般。荷西的紀錄片應該歸類於劇情片,它是那麼的主動,帶領著觀眾去收下導演所想要傳達的訊息。

成長在平凡環境中的主角--提姆,就像隨處可見的平凡人一般,在平凡的家中長大,起伏於不上不下的課業成績,沒什麼可吸引旁人目光的特點。唯一擅長的是游泳與跳水,憑藉這個專長申請到一流的大學獎學金,以為從此就像人造衛星開始繞行上了軌道,卻交了壞朋友染上酗酒的惡習,背受了傷,退了學。

在外頭浮沈的提姆,有強烈表演欲望,曾經努力爭取演出的機會,但也都僅限於跑跑龍套般的角色,以為人生從此就像滑下一無止境的緩坡沒什麼希望時,最後在阿拉斯加的曠野裡找到了舞台--灰熊保護者。

鏡頭前,荒野中,那麼的真誠與直率,生動的錄下與灰熊的互動,藉由推廣生態保育的觀念與勇敢和灰熊接觸的形象,讓他成為家喻戶曉的人物。13年的夏日時光,沒有間斷的紀錄與觀察灰熊,這不是簡單的表象的動機可以維持的,需要有一種類似宗教般的信仰。

當他反覆的說他愛那些熊時,我相信那一定是真的,沒有任何虛假,因為他早已變成灰熊,灰熊就是他,他是那麼的嚮往灰熊的世界,迫不急待想融入,認定那裡盡是美好與純真。但這種東西不存在於現實的世界,盜獵者與廣告商侵入了他的領域,管理處的各項規定,熊群間的殘忍、天候的異常,提姆越來越憤怒。

沒有起伏的人生與情緒都不是提姆想要的,他放棄服用鎮定劑,冒險的與灰熊共處,生活在充滿各種表情的日子裡,最後被黑暗所吞食,他應該沒有悔恨,唯一可惜的,是捲入了一個悲劇的女友。片尾,提姆帶領著兩隻小灰熊沿著河越走越遠,另一個世界等待著。