2013年5月31日 星期五

Objective-C while Statement, do, break, continue

for (initial expression; loop condition; loop expression)
   program statement


initial expression;
while (loop condition)
{
   program statement
   loop expression;
}

for, while 相似,如果能用for寫出來,也可以用while改寫,依照不同的狀況選擇用哪一個,讓程式可讀性較佳。大原則是如果事前知道要做幾次,那用for,反之則使用while。

看個例子,先用while來寫

        int theSecretNumber = 77;
        int yourAnswer = 0;
        
        while (yourAnswer != theSecretNumber) {
            NSLog(@"請猜一個介於1~100的數字");
            scanf("%i",&yourAnswer);
        }
    
        NSLog(@"恭喜你猜到了");

改用for,你覺得那個可讀性較好呢??

        for (int theSecretNumber = 77,yourAnswer = 0; 
             theSecretNumber != yourAnswer; ) {
            NSLog(@"請猜一個介於1~100的數字");
            scanf("%i",&yourAnswer);
        }
        NSLog(@"恭喜你猜到了");

這個例子很像都差不多,Orz.....
就邏輯上來說,因為事先不知道到底猜幾次才會猜中,所以選擇while應該是比較適合的。

do statement
先做了再說,不管while裡頭的判斷如何,起碼做一次

do
    program statement
while ( expression);


看個範例:
int main(int argc, const char * argv[])
{
    @autoreleasepool {
        
        BOOL (^ask) (void)=  ^{
            BOOL answer;
            char c;
            NSLog(@"請問你願意被我揍嗎??");
            scanf("%c%*c",&c);
            if (c == 'y') {
                answer = YES;
            }else{
                answer = NO;
            }
            return answer;};
        
        
        int hitNumber = 0;
        
        do {
            hitNumber++;
            NSLog(@"揍了第%d拳",hitNumber);
        } while (ask()==YES);
        
        NSLog(@"Game over");
        
    }
    return 0;
}


這個例子中,在還沒有詢問他要不要被揍之前,就先打了,之後才根據他的回答做動作,這邊用了^{Block}實作詢問的行為,以後會再提怎麼用。


break,  continue

不管在for 或是while迴圈,都可以使用這兩個關鍵字去控制流程,break直接跳離開迴圈,continue則是以下的這次不執行,但繼續迴圈。
        for (int i = 1; i<=5; i++) {
            NSLog(@"第%d拳",i);
            if (i==3) {
                break;
            }
            NSLog(@"打中");
        }



        for (int i = 1; i<=5; i++) {
            NSLog(@"第%d拳",i);
            if (i==3) {
                continue;
            }
            NSLog(@"打中");
        }



這一篇筆記有點暴力了

Objective-C for Statement


for( initial expression ; loop condition ; loop expression ){
     statement 1;
     statement 2;
}

如果statement只有一句,大括號可以省略。

看個簡單的例子,從1加到100
int sum = 0;
for (int i = 1; i <= 100; i++) {
    sum = sum + i;
}

如果停止的判斷語句沒寫或沒寫好,就變成無窮迴圈 :)
for ( ; ; )
   NSLog(@"I'm infinitive loop!!");

for (int i = 1; i > 0 ; i++)
   NSLog(@"I'm infinitive loop!!");

在判斷時可以使用的 Operator:
==   等於
!=    不等於
<     小於
<=   小於等於
>     大於
>=   大於等於

需要注意的是 == 不要寫成了=

而有些寫法可以讓程式看起來更簡潔一些
sum += i;   (sum = sum + i;)
i++;           ( i = i +1; )
i--  ;           ( i = i - 1; )

賦值時:
i++     先賦值後加一
 ++i    先加一後賦值

        int x = 0;
        int y = 0;
        
        y = x++;
        
        NSLog(@"x is %d",x);
        NSLog(@"y is %d",y);

        int x = 0;
        int y = 0;
        
        y = ++x;
        
        NSLog(@"x is %d",x);
        NSLog(@"y is %d",y);



可以用 scanf這個函式去讀取使用者從鍵盤上的Keyin
        int guessNumber;
        NSLog(@"這期大樂透你選的數字是:");
        scanf("%i",&guessNumber);
        NSLog(@"您選的數字是:%i",guessNumber);

for裡頭的變數與更新運算可以不只一個
for (int i=1, j=10; i<j; i++,j--) 
    NSLog(@"i = %i, j = %i",i,j);



之後可能會常見到這種寫法,很像其他語言中的foreach,非常好用
NSArray *box = [[NSArray alloc]initWithObjects:@"第一個物件",
                                                   @"第二個物件",
                                                   @"第三個物件", nil];
    for (id i in box) 
        NSLog(@"%@",i);
    


最後提一下巢狀的for loop,簡單的九九乘法表

for (int i = 2; i <=9; i++) {
        for (int j = 2; j<=9; j++) {
            NSLog(@"%d * %d = %d",i,j,i*j);
        }
}


2013年5月30日 星期四

Objective-C 物件導向基礎

Objective-C 是架構於C語言之上的,導入了物件的觀念,Object-oriented Programming (OOP)有許多的特色,如繼承,封裝,多態等等,這不是三言兩語可以說完的,還是回歸正題,簡單地介紹在objective-c 中的類別與物件基本語法。

看個例子:

Wolf.h
#import <Foundation/Foundation.h>
@interface Wolf : NSObject
{
    NSString *_name;
    int _age;
}

-(void)SetName:(NSString *)name;
-(NSString *)name;
-(void)SetAge:(int)age;
-(int)age;
+(void)bray;

@end

在@interface與@end中間宣告這個類別有哪些{變數}與方法,而Wolf是這個類別的名稱,後面的冒號表示繼承於誰,NSObject是所有類別的最源頭,方法前面的符號 - 表示物件方法, + 表示類別方法。

類別命名規則,第一個字通常是大寫,常看到的NS表示NextStep,你也可以取自己的前綴字,如BH, WTF....。變數的命名開頭習慣小寫,不可以使用數字為開頭,名稱不能用保留關鍵字(int, float....),特殊符號(%,#..... 註一)與空格也是禁止的。另外在Objective-C 中,是區分大小寫的。

 - (void)SetName:(NSString *)name;
這是個物件方法,沒有回傳值,方法名稱是SetName:,如果有接受參數必須接著冒號,接受一個NSString參數。

為了維持物件導向的封裝性(Encapsulation),讓外部的類別不能直接存取與修改變數,所以我們不得不設計這麼多的getter and setter,這也是為什麼後來會有@property來減輕工作量的原因。

Wolf.m
#import "Wolf.h"

@implementation Wolf
-(void)SetName:(NSString *)name
{
    _name = name;
}
-(NSString *)name
{
    return _name;
}
-(void)SetAge:(int)age
{
    _age = age;
}
-(int)age
{
    return _age;
}
+(void)bray
{
    NSLog(@"Wu Wu.......!!!");
}

@end

在@implementation與@end中,把宣告的方法都實作出來,這裡可以看到為什麼很多人的習慣是把變數前面加下引號,因為如果不加下引號,會寫成 name = name,容易混淆,可讀性變差。
main.m
#import <Foundation/Foundation.h>
#import "Wolf.h"
int main(int argc, const char * argv[])
{
    @autoreleasepool {
        [Wolf bray];
        Wolf *w = [[Wolf alloc]init];
        [w SetName:@"BloodX"];
        [w SetAge:7];
        
        NSLog(@"%@ is a wolf, and he is %d years old.",[w name],[w age]);
    }
    return 0;
}

[Wolf bray],類別方法可以直接呼叫,而不需要實體化一個物件。
Wolf *w = [[Wolf alloc] init], 宣告且生成一個物件,alloc 配置一塊記憶體,init初始化。這些方法都是繼承至NSObject。

當然你也可以這樣寫,如果時間多的話。
Wolf *w;
w = [Wolf alloc];
w = [w init];

時間少的話,可以偷懶一下
Wolf *w = [Wolf new];

方法的呼叫是用中括號,你也可以一直刮下去,例如:[[[[Wolf allo ]init ]play ],但可讀性就變差了。



@property  and @synthesize

在Objective-C 2.0之後開始可以使用@property來幫忙我們少寫一點getter and setter,使用方式很簡單,在.h 檔中宣告,.m 合成。

Wolf.h
@interface Wolf : NSObject
@property NSString *name;

-(void)bray;

@end



Wolf.m
@implementation Wolf
@synthesize name;
-(void)bray
{
    NSLog(@"Wooo Wooo....");
}


這樣就幫你設定好並實作出兩個方法,
-(NSString *)name;
-(void)setName;

如果你不想讓別人知道實際修改的是什麼變數,可以改成這樣。

.h
@interface Wolf : NSObject
{
    NSString *name;
}
@property NSString *secret;

-(void)bray;

@end

.m
@implementation Wolf
@synthesize secret = name;
-(void)bray
{
    NSLog(@"Wooo Wooo....");
}

@end


. dot operator  
方便直接呼叫 getter and setter

Wolf *w = [Wolf new];        
w.name = @"Jon";               //  在等號左邊,呼叫setter  [w setName:(@"Jon")]
NSLog(@"%@",w.name);  //                           呼叫getter  [w name]

註一:在書中說 $ 字元不可以於命名中使用,不過實際在編譯時卻可以 :) 。

2013年5月29日 星期三

Class Extensions Example -- add private function

上一篇講到Categories的用法時,提到了它無法新增var,那如果我要新增var又該怎麼辦,這時候class extension就派上用場了,class extension 和category很像,但又有一些不同,class extension又稱為匿名的category。

相同點:都用來修改現有的類別
不同點:
  1. Category 只能新增或修改方法,無法新增變數
  2. Category 中所宣告的方法不用一定要全部實作,但是class extension宣告的方法都必須實現
  3. Category 方法的implement code必須放在原本類別的.m檔中
  4. Class extension 所宣告的var and function都是private

使用時機:引用Apple官方文件,“Class extensions are often used to extend the public interface with additional private methods or properties for use within the implementation of the class itself.”

以上篇的Dog為基底,我們來擴充一下,新增一個Class extension檔,輸入Extension name


這時候只會產生一個.h檔 Dog_Identification.h,因為要在原本的類別中實作


Dog_Identification.h
#import "Dog.h"

@interface Dog ()
{
    NSString *_secret;
}

-(NSString *)secret;
-(void)setSecret:(NSString*)s;

@end

括弧內沒有東西,所以這也是為什麼它稱為 匿名的Category的原因。


在原本的Dog.m檔中import此標頭檔,記得不是在Dog.h,因為如果你在Dog.h import Dog_Identificaton.h,又在Dog_Identification.h import Dog.h,這樣互相import會出錯的。


Dog.m
#import "Dog.h"
#import "Dog_Identification.h"

@implementation Dog

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

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

-(NSString *)secret
{
    return _secret;
}
-(void)setSecret:(NSString*)s
{
    _secret = s;
}

-(NSString *)giveMeTheSecret
{
    return [self secret];
}

@end

實作完Class extension functon,另外在Dog.h宣告一個 giveMeTheSecret方法,去調用 -(NSString *)secret 這個private function,因為外部的類別是無法使用private function的。 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];
        NSLog(@"The secret is %@.",[d giveMeTheSecret]);
    }
    return 0;
}


BTW,很多人在使用Class Extension時,是不會去產生一個.h檔的,而是直接在原本類別的.m檔中去做宣告,所以我們這個範例可以改為如下,把Dog_Identification.h刪掉

Dog.m

#import "Dog.h"

@interface Dog ()
{
    NSString *_secret;
}

-(NSString *)secret;
-(void)setSecret:(NSString*)s;

@end


@implementation Dog

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

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

-(NSString *)secret
{
    return _secret;
}
-(void)setSecret:(NSString*)s
{
    _secret = s;
}

-(NSString *)giveMeTheSecret
{
    return [self secret];
}

@end




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)