2013年8月23日 星期五

Uva 100 - The 3n+1

問題網址
有幾點要考慮:
  1. 讀取的行數不是單行
  2. 每行的兩個數值,不一定是前小後大
#include <stdio.h>
int cycleLength(int number);
int main()
{
  int i, j, input1, input2;
 
  while( scanf("%d %d",&input1, &input2) != EOF ){
  
      //檢查與調整前後大小
      if(input1 < input2){
          i = input1;
          j = input2;
      }else{
          i = input2;
          j = input1;
      }
  
  
      int maxCycleLength = 0;
      long int x;
      
      for ( x = i; x <=j ; x++){
          int c = cycleLength(x) ;
          if (c > maxCycleLength){
              maxCycleLength = c ;
          }
      }
      printf("%d %d %d\n",input1,input2, maxCycleLength );
 }
 
 
 return 0;
}

int cycleLength(int number)
{
    int c = 1;
    while(number!=1){
        if(number%2==1){
            number = 3*number +1;
            c++;
        }else{
            number = number/2;
            c++;
        }
    }
 return c;
}


解了此題,但是速度太慢(run time:0.612),要嘗試用別種方法

2013年8月16日 星期五

Objective-C Protocols

要求類別一定要實現某些方法時,需要用的到 protocol,代理人模式中 (delegate)常常見到,因為類別在成為另一個類別的代理時,必須實作一些方法以達成代理人的角色。而Objective-C中,因為沒有多重繼承,利用 protocol可以使一個類別具有多重型別的方法。

舉個例子,我們可以定義一些 protocols,@protocol 和 @end 之中宣告要實現的方法,方法的預設值都是required,使用關鍵字@optional將它之下的變成不是必須的,如果是使用新增檔案的方式加入protocol,預設這些 protocols也遵守 (<>) NSObject這個 protocol 。

Behavior.h

@protocol <NSObject>
@optional
-(void)fly;
@required
-(void)run;
-(void)sleep;
@end

Think.h

@protocol <NSObject>
-(void)getRabbit;
-(void)escape;
@optional
-(void)calculate;
@end

然後建立一個類別 Dog , conform/adopt  these protocols,有多個 protocols,用逗點隔開

Dog.h

@interface Dog : NSObject <Behavior, Think>

@end

Dog.m

@implemention Dog
-(void)run
{
    ........
}
-(void)sleep
{
    ........
}
-(void)getRabbit
{
    ........
}
-(void)escape
{
    ........
}
@end

想確認某個物件是否confirm protocol,可以這樣作

id someObject;
......
if ([someObject confirmsToProtocol : @protocol(Think) ] == YES){
    // do something
}

用來宣告型別遵守某些 protocol

id <protocol3, protocol2> someObject ;

2013年8月15日 星期四

Objective-C -- 多型 Polymorphism

polymorphism is a programming language feature that allows values of different data types to be handled using a uniform interface. The concept of parametric polymorphism applies to both data types and functions.
.. from wiki

透過相同的界面進行不同的操作,多型可以應用在資料型別與方法上

書中的解釋更為簡單,Same name, Different Class,不同的類別,相同的名稱,舉個例子,就像是同樣都是叫,鴨子類別中的叫可能是呱呱呱,狗的類別的叫則是汪汪汪。

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

@interface Animal : NSObject
{
    NSString *_name;
}
-(void)bray;
@end
Animal.m
#import "Animal.h"

@implementation Animal

-(void)bray
{
    NSLog(@"A......");
}

@end

Duck.h
#import "Animal.h"

@interface Duck : Animal

@end
Duck.m
#import "Duck.h"

@implementation Duck

-(void)bray
{
    NSLog(@"Caw..Caw..Caw.....");
}

@end
Dog.h
#import "Animal.h"

@interface Dog : Animal

@end
Dog.m
#import "Dog.h"

@implementation Dog

-(void)bray
{
    NSLog(@"Wow...Wow..www");
}

@end
main.m
#import <Foundation/Foundation.h>
#import "Animal.h"
#import "Duck.h"
#import "Dog.h"

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

    @autoreleasepool {
        
        id foo;
        //First subclass
        foo = [[Duck alloc]init];
        [foo bray];
        //Second subclass
        foo = [[Dog alloc]init];
        [foo bray];
        
    }
    return 0;
}

輸出結果:




main.m 當中的 id 是objective-C中泛指所有類別的型別,在其他的語言中,如果沒有這種型別,id 改為 Animal一樣可以,而在Objective-C中兩者都可以使用。
foo變數先指到Duck之後指向Dog,雖然方法名稱都是一樣,但是會根據個別類別中的方法去表現。多型的優點就是,不需要取很多的方法名稱,鴨子叫,狗叫,貓叫...,只要執行時去判斷是哪個類別,在找出相對應的方法即可。

Dynamic typing, dynamic binding

前者是指在執行的過程中,物件動態決定屬於哪個類別,後者則是根據哪種類別去執行方法,用上述的例子來說明

foo = [[Dock] alloc] init];   // dynamic typing
[foo bray];                         // dynamic binding

In runtime決定型別固然很強大,但也有缺點,首先在編譯時,complier無法檢查是否有相對應的方法存在,相反地,如果是用靜態型別 static typing,complier 會確保每個方法對應到正確物件,這可以讓我們在編譯時就發現錯誤而不是在執行的時候,因為你永遠不知道使用者會如何操作,意謂者你不可能測試完所有的可能 ; 再者,使用大量的  id,會讓程式的可讀性降低。

2013年8月14日 星期三

Objective-C Inheritance

繼承總要有個來源,在Objective-C 中, NSObject 是所有的類別的源頭,所以時常可以看到
@interface ClassA : NSObject
    ................
@end

self, super
-(id)init
{
    self = [super init];
    if(self) {
        // Initialize code here
    }
    return self;
}

在建構子中,我們常常這樣寫,self 表示自己,super則是父類別,而這段程式是先使用父類別的建構子,如果生成成功,進行子類別的初始化工作,最後回傳。

@class

在類別中要使用別的類別,通常我們都在.h檔中去import另一個類別的標頭檔,
#import "ClassB.h"  // ClassA.h
也可以使用@class這個關鍵字去告訴complier,會更有效率的多
@class ClassB     // ClassA.h
這裡會用到ClassB這個類別,你不用知道它的內容是什麼,儘管編譯過就是了。但事情沒這麼簡單,如果在ClassA.m檔中,有用到ClassB的方法,這時候編譯器怎麼會知道ClassB中的方法內容,就會出現錯誤。



把ClassA.m 檔中匯入ClassB.h檔就可以解決這個問題

#import "ClassB.h"   // ClassA.m

那為何不一開始就在ClassA.h中,直接import ClassB.h檔就好了呢,是啊!!這樣不是多此一舉嗎~~所以使用時機是當你不會用到另一個類別的方法時,用 @class節省編譯很多時間。

另外,有一種情況是非用不可,那就是 circular inclusions,
#import "ClassB.h"  // ClassA.h
#import "ClassA.h" //  ClassB.h

這裡就需要用到@class,就不會再編譯的階段一直無窮的交互import。下面的例子,因為都有用到對方的方法,所以在.m中都需要 import 對方的.h檔。

ClassA.h
#import <Foundation/Foundation.h>
@class  ClassB;
@interface ClassA : NSObject
{
    NSString *name;
    ClassB *b;
}

-(NSString *)name;
-(NSString *)theNameOfB;

@end
ClassA.m
#import "ClassA.h"
#import "ClassB.h"

@implementation ClassA
-(id)init
{
    self = [super init];
    if (self) {
        name = @"A";
    }
    return self;
}

-(NSString *)name
{
    return name;
}
-(NSString *)theNameOfB
{
    if (!b) {
        b = [[ClassB alloc]init];
    }
    return b.name;
}
@end

ClassB.h
#import <Foundation/Foundation.h>
@class ClassA;

@interface ClassB : NSObject
{
    ClassA *a;
    NSString *name;
}

-(NSString *)name;
-(NSString *)theNameOfA;

@end
ClassB.m
#import "ClassB.h"
#import "ClassA.h"

@implementation ClassB


-(id)init
{
    
    self = [super init];
    if (self) {
        name = @"B";
    }
    return self;
    
}

-(NSString *)name
{
    return name;
}

-(NSString *)theNameOfA
{
    if (!a) {
        a = [[ClassA alloc]init];
    }
    return a.name;
}

@end

main.m
#import <Foundation/Foundation.h>
#import "ClassA.h"
#import "ClassB.h"

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

    @autoreleasepool {
        ClassA *a = [[ClassA alloc]init];
        NSLog(@"ClassA use classB method, the name of ClassB : %@",[a theNameOfB]);
        
        ClassB *b = [[ClassB alloc]init];
        NSLog(@"ClassB use classA method, the name of ClassA : %@",[b theNameOfA]);
    }
    return 0;
}


輸出結果

2013年8月13日 星期二

Objective-C Enumerated Data Types

enum 

列舉的優點就是將整數與符號名稱作連接,讓程式的可讀性變好,容易維護,而不需要去記某個數字代表的意義。
enum color{ red = 1, orange, yellow, green, blue, indigo, purple};

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        
        enum color displayColor = blue;
        NSString *displayColorStr;
        
        switch (displayColor) {
            case red:
                displayColorStr = @"紅";
                break;
            case orange:
                displayColorStr = @"橙";
                break;
            case yellow:
                displayColorStr = @"黃";
                break;
            case green:
                displayColorStr = @"綠";
                break;
            case blue:
                displayColorStr = @"藍";
                break;
            case indigo:
                displayColorStr = @"靛";
                break;
            case purple:
                displayColorStr = @"紫";
                break;
                
            default:
                displayColorStr = @"數值有誤";
                break;
        }
        
        NSLog(@"目前的顯示器顏色為:%@",displayColorStr);
    }
    return 0;
}

Anonymous enum 匿名列舉

enum {red = 1, orange, yellow, green, blue, indigo, purple} displayColor ;

沒有名稱,變數緊接著宣告,使用時機,當你懶得取名稱時 :) (大誤)
而宣告時一直打enum也挺煩的,這時可以用 typedef 關鍵字。

typedef

typedef enum {red = 1, orange, yellow, green, blue, indigo, purple} Color ;

Color display = red;

2013年8月9日 星期五

Objective-C Variables

Local Variables 

區域變數只存活於它所在的區塊內,能存取它的範圍也在那個區域,而程式執行完那段後,變數即消失。

-(void)saveMoney
{
     int capital = 0;
     capital++;
     NSLog(@"Capital : %d",capital);
}
每當saveMoney這個方法被呼叫時,變數在此初始化並賦值,然後加一,而在這個區域中,可以存取到capital變數,但是不管執行saveMoney這個方法幾次,Capital永遠都是1,因為方法執行完後,變數capital就消失了。如果要保留此變數所存的值,勢必要使用別種方法。

static

-(void)saveMoney
{
     static int capital = 0;
     capital++;
     NSLog(@"Capital : %d",capital);
}
如果我在變數前面加上關鍵字 static,那麼這個變數只會被初始化並賦值一次,並不會每次呼叫此方法時都重複作一次,故第一次執行saveMoney時,initialize capital and add 1 ,第二次加一,第三次再加一,一直下去。

不過這樣的寫法,capital只能在saveMoney中被存取,把它移到implement外,這樣在implement中的其他方法也都可以存取它囉,邏輯上也更清楚。

#import "Cheapskate.h"
static int capital;
@implemention Cheapskate
-(void)saveMoney
{
     static int capital = 0;
     capital++;
}

-(void)showMoney
{
     NSLog(@"Capital : %d",capital);
}
@end

Instance  Variable Scope

@protected      self and subclass (default)
@private        self
@public         any other class
如果是public instance variable,可以使用 pointer operator ( ->)

Global Variable

在程式的開始,任何方法、類別宣告(@interface)、struct declaration之外,可以宣告與定義全域變數,變數的名稱前面慣例加上g,表示全域。全域變數的使用要很小心,除錯時很難找,儘量少用,如果真的要用,名稱要取的好一些,建議前面加上g。

int gVariable = 0 ;


如果只有declaration沒有definition的話,基礎型別用預設值,int 0,objective-C object則是nil,如果是複雜的definition,無法在宣告的同時也定義,可以在其他地方作定義,例如,main()、application delegate init method, applicationDidFinishLaunching method ..

NSArray *array = [[NSArray alloc] initWithObjects:@"one",@"two",nil]; 
// Initializer element is not a compile-time constant
如果在 global variable 前面加上 static ,那這個變數的可見域就只限於此 implementation file,不再是全域變數了,變成了像是 class variable

External Variables

外部變數,顧名思義就是宣告在外的變數,:(,通常和全域變數一起用,告訴complier這個變數已經在外部宣告並定義過了,我在這個檔案或區域中要使用。記得,在使用extern 時,不可以再定義。

extern int gVariable = 10; // error

const 

無法再修改值的變數,這意味著在開始的時候就必須給定值,良好的編程習慣是,當你知道某個變數的值不應該再變動時,就加上const,別人也一目了然。

const double pi = 3.14159;

volatile

揮發易變的變數,使用的原因是為了預防編譯器把變數最佳化,以下是wiki的例子:

static int foo;
void bar(void) {
    foo = 0;
 
    while (foo != 255)
         ;
}
similar to this
void bar_optimized(void) {
    foo = 0;
 
    while (true)
         ;
}
foo變數有可能在外部會進行修改(例如:delegate),加上volatile 避免編譯器的最佳化。
static volatile int foo;
 
void bar (void) {
    foo = 0;
 
    while (foo != 255)
        ;
}

First Father's Day

Dear 小米,

昨天是阿爸我的第一個父親節,雖然你還不會開口說話,不過在我抱著你時,你嘴角微微上揚的表情已經讓我十分開心,一掃整日的陰霾。

阿爸的手機今天當掉了,壞得很徹底,我並不心疼系統壞掉,而是難過失去了你的一些珍貴照片,從你出生後,大部分都是用手機幫你拍照,生產房、醫院、月子中心,全部不見了我會很內疚的,還好系統有自動備份照片30天的功能,所以最後只有幾張在產房的照片找不回來。經過這次的教訓,阿爸我會常常備份照片,也會挑選幾張不錯得上傳至部落格,也打算買一台好一點的相機幫你記錄。

剛出生,護士阿姨幫你洗好澡戴上帽子的模樣

每個新生兒都是一直睡,你也不例外,
3440克的體重,看起來非常可愛。

睡覺的時候不喜歡被包的緊緊的你,最愛伸出手,
夢想著拯救世界。

阿屋

衝啊

休息一下

終於有一張是睡醒的模樣

就是跪著也要繼續睡

天使
擦上眼藥膏,泛著淚光

月子中心的嬰兒房總是亮著燈,
阿爸我知道你還想睡。

護士阿姨把你照顧的很好,白白淨淨的


2013年8月8日 星期四

Objective-C DataTypes

Basic data types:

  • int                20          %i
  • float             20.5f      %f
  • double         2.2e-10  %e  /  %g  ( %g讓系統判斷是否使用科學記號,<-4>5 )
  • char            'W'          %c
double 的精準度大概是 float 的兩倍,Complier會自動將有小數點的數值視為double,如果要強制為float,在數值後面加上f (7.65f)。把char連起來就是character string,而以這些型別的數值或字串都是constant,如 8, 8.6, 'x', @"Objective-c"。

                     前綴字     例          十進位數值            NSLog format characters
八進位     /   0           /   040     /     8*4 = 32                        %o
十六進位 /   0x         /   0x2F   /     2*16 + 15 = 47              %#x


Qualifiers:

  • long             根據系統擴充儲存的bit,
                              在大多數系統裡,long int 、int 都是32bits。  21000800L      %li
  • long long      保證至少64bits  %lli
  • short            少一點bits,不過不會少於16bits   %hi
  • unsigned      都是正數   10000U
  • signed          預設值


Type id
id為任意型別,在多型與動態綁定中用的很多。

備註:表示Example表示NSLog chars。

2013年8月7日 星期三

Python 學習筆記


  • 中文出現錯誤,在程式碼開頭加上:

          # encoding: utf-8


  • Before      Python 3.0      After    

          raw_input  取得字串    -->   改名為 input
          input          取值            -->   取消,可以改用 eval(input())

2013年7月18日 星期四

Django 學習筆記

最近因為需要架設一個動態網站,而又剛好在學習Python,所以選擇了Django作為開發的工具,這裡記錄了架設過程中的學習歷程。

開始總要有個方向,當然從網路上有人寫過得文章開始,高見龍的介紹簡單容易懂,官方的教學網站,再來就是一些電子書


  • MySQL 中文亂碼,在新增或修改時,發生類似如下的錯誤提示: 
    Incorrect string value: '\xE5\x93\x88\xE5\x93\x88...' for column 'content' at       row 
    解決方案

       如果照著上述的解決方案,再重啟mysql時會發生錯誤,現在新版的mysql不能這樣設定        my.cnf,只要在create database時,宣告使用utf8就可以解決,刪掉舊的db,新建一個吧!

      CREATE DATABASE `test`CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';

      參考


  • Python 程式碼中,如果有中文,記得在文件開頭先宣告

       # -*- coding: utf-8 -*-


  • MySQLdb 操作時發生,中文無法新增或修改時,

       "UnicodeEncodeError:'latin-1' codec can't encode character ..."

        解決方案


  • query insert 中,因為每個欄位都要給值,系統預設的primary key , auto_increment的值,該輸入什麼呢,可以去找出現在的最大值加上一,也可以使用關鍵字  "DEFAULT",會自動加上去。

2013年7月5日 星期五

小米來囉

阿姆工作到星期三總算到了一個段落,凌晨三點鐘,小金感覺有股熱流從下腹流出,問說不會是羊水破了吧!我看看時間,回說應該是妳尿失禁,她狠狠看了我一眼,讓我知道該出發去醫院了。晚上無人的街道很安靜,偶而有一台車呼嘯而過,如果沒有急事,我倒很想享受這份寧靜。待產房有很多間,但是都沒有人,羊水慢慢地流,伴隨著腹中胎兒的心跳聲,我們兩個繼續入睡,等待陣痛的到來。
早上陸陸續續有人來看妳,先是護士阿姨幫妳媽內診評估現在開了幾指,後來醫生阿杯看了看,説最快也要傍晚,有可能會拖到半夜噢!妳媽嚇傻了,雖然現在陣痛還可以,但要熬這麼久也不是一件舒服的事情。催生的藥水開始發揮作用,八點時有稍微一點的痛,過了一小時,妳老媽忍不住要打無痛分娩,雖然不到一指讓她有點不好意思,但也顧不了這麼多了。
無痛分娩與剖腹生產真是近代醫學的一大進步,很快的開了三指,劑量也由10調到12,接近中午已經四指半,醫生阿杯也被緊急呼叫過來,大家都很驚訝怎麼快,都説是催生藥水的功效,而妳媽總説是妳孝順,不忍心讓妳阿姆痛這麼久。11:48妳和我們見面了。
妳生日的今天是美國獨立紀念日,妳阿爸我擅自幫妳取了個小名,「小米」,因為米國人嘛,希望妳喜歡。




2013年6月18日 星期二

彌月蛋糕試吃心得

因為女兒就快要出生,所以最近都在找適合的彌月蛋糕,去了家附近的幾家蛋糕店,每家店給我的印象都不太一樣。

第一家,xx鄉,以蜂蜜蛋糕出名,彌月禮盒很可愛,是我喜歡的包裝,不過跟店員說我們想試吃時,她隨手拿了個塑膠袋,裝了兩三塊餅乾就遞給我們,讓我們感覺很差,很像我們是來騙東西吃得。

第二家,xx屋,有專門設計的小禮盒裝試吃的小蛋糕,和幾塊餅乾,比前一家有進步,蛋糕不錯,列為考慮之一。

第三家,喜憨兒烘焙屋,店員非常的客氣,留下了我的聯絡方式,說會幫我預定試吃禮盒,幾天後可以過來拿,我心想不就現場給我就好了嘛,幹麼這麼麻煩,回家後我馬上收到她們的來信,提醒我哪一天去拿試吃禮盒。幾天過後,因為太忙的緣故,我比原訂日期晚了兩天才去,本以為應該沒有了,不過她們說還保留著,但不能給我,是要懲罰我忘了這件事情嗎??不是,是因為過了最佳品嚐時間,怕口感變了,影響我的感覺。昨天晚上,我終於拿到了喜憨兒烘焙屋的彌月試吃禮盒,一塊小蛋糕,三塊餅乾,這雖然不是我吃過最好吃的蛋糕,但我真的非常非常的喜歡,因為充滿了濃濃的尊重與祝福。




2013年6月4日 星期二

Objective-C Pointer

Objective-C中,呼叫方法時,參數的傳遞是 call by value,也就是複製一份值傳遞進去,網路上的有些資料提到類似 call by reference,其實骨子裡還是 call by value。而要解釋這個概念前不得不先解釋指標(Pointer)。

指標 Pointer

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        NSString * ptr = @"Content";
        NSLog(@"The value of ptr : %p",ptr);
        NSLog(@"The address of @\"Content\" : %p",@"Content");
        NSLog(@"The address of ptr : %p",&ptr);
    }
    return 0;
}

我們先來看這一行,NSString * ptr = @"Content"; ,這裡宣告了一個指標ptr,指向一個NSString的物件,而這個物件的值是@"Content",數值於電腦運算時,都存儲於記憶體中,可以想成一條馬路上有許多房子,大小固定,都有門牌,而物件有大有小,有些可能需要好幾間房子打通才放得下。示意圖如下,Ptr所存的值( 0x100001040 )剛好是這個字串的記憶體位置,ptr本身也有個Address是  0x7fff5fbff898。由這個例子可以知道要知道門牌位置必須使用 & 取址運算子,在NSLog中用%p列印出來。



那你也可能會問,ptr這個指標名稱又是存在哪裡了,應該也需要一塊記憶體去記錄吧!!沒錯,它在編譯執行後,作業系統記錄了它的名稱與Address,當你需要用ptr時,系統去找有沒有ptr這個東西,有的話又在記憶體的哪裡,這個你沒有辦法操作,也遠離了今天的主題,所以我們暫不討論。

&取址運算子,告訴我這個變數的記憶體位置。
*取值運算子,告訴我這個變數所指過去的物件值是什麼。

現在可以來看看方法參數的傳遞是如何運作的:

#import <Foundation/Foundation.h>

void someOperate(NSString *m);

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

    @autoreleasepool {
        NSString *o = @"Origin";
        NSLog(@"The address of o : %p",&o);
        NSLog(@"The value of o : %p",o);
        NSLog(@"The address of @\"Origin\" : %p",@"Origin");
        someOperate(o);
        NSLog(@"%@",o);
        NSLog(@"The address of o : %p",&o);
        NSLog(@"The value of 0 : %p",o);
    }
    return 0;
}

void someOperate(NSString *m)
{
    NSLog(@"**********");
    NSLog(@"The address of m : %p",&m);
    NSLog(@"The value of m :%p",m);
    m = @"Modify";
    NSLog(@"After");
    NSLog(@"The address of m : %p",&m);
    NSLog(@"The value of m :%p",m);
    NSLog(@"The address of @\"Modify\" : %p",@"Modify");
    NSLog(@"**********");
}



用一張圖來解釋發生什麼事情
當我們把o指標傳進方法後,複製了它的值給了m,而在方法中  m = @"Modify",又產生了一個物件NSString,m的值變成了@"Modify"的記憶體位置,所以不管我們在方法中怎麼改,原本的值不會有任何變化。

那如果我們想要的是直接更改原本的值,該怎麼做呢。有幾種方法,先講最容易的,把NSString改為NSMutableString,這樣在改值時,就會把原先的值,在原位置直接改變,而不是新產生一個新的物件。

#import <Foundation/Foundation.h> 

void someOperate(NSMutableString *m);

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

    @autoreleasepool {
        NSMutableString *o = [[NSMutableString alloc]initWithString:@"Origin"];
        NSLog(@"The address of o : %p",&o);
        NSLog(@"The value of o : %p",o);
        someOperate(o);
        NSLog(@"%@",o);
        NSLog(@"The address of o : %p",&o);
        NSLog(@"The value of 0 : %p",o);
    }
    return 0;
}

void someOperate(NSMutableString *m)
{
    NSLog(@"**********");
    NSLog(@"The address of m : %p",&m);
    NSLog(@"The value of m :%p",m);
    [m setString:@"Modify"];
    NSLog(@"After");
    NSLog(@"The address of m : %p",&m);
    NSLog(@"The value of m :%p",m);
    NSLog(@"**********");
}


但是如果沒有辦法更改為Mutable的類別時該怎麼辦,這時候有個迂迴的用法,指標的指標,既然無法更動指標所指的值,那就變動指標所存儲的記憶體Address,看一段代碼。

#import <Foundation/Foundation.h> 
void someOperate(NSString **m);

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

    @autoreleasepool {
        
        NSString *o = @"Origin";
        
        NSLog(@"The address of o : %p",&o);
        NSLog(@"The value of o : %p",o);
        NSLog(@"The address of @\"Origin\" : %p",@"Origin");
        
        someOperate(&o);
        
        NSLog(@"%@",o);
        NSLog(@"The address of o : %p",&o);
        NSLog(@"The value of o : %p",o);
    }
    return 0;
}

void someOperate(NSString **m)
{
    NSLog(@"**********");
    NSLog(@"The address of m : %p",&m);
    NSLog(@"The value of m :%p",m);
    NSLog(@"The value of *m : %p",*m);
    
    *m = @"Modify";
    
    NSLog(@"After");
    NSLog(@"The value of *m : %p",*m);
    NSLog(@"The address of m : %p",&m);
    NSLog(@"The value of m :%p",m);
    NSLog(@"The address of @\"Modify\" : %p",@"Modify");
    NSLog(@"**********");
}


這邊宣告一個變數o指向@"Origin",o的記憶體位置是f898,o的值是1048,也就是@"Origin"的記憶體位置,someOperate方法接受的參數為指標的指標,而也就是o指標的Address,傳入後,發現它複製了一份o指標,m所指的位置並不是o,接著把m所指向的值由1048改為1168 ( *m = @"Modify"),到目前為止都懂,奇妙的是原本o所存儲的值也跟著變了,藉由這個方式,可以在方法中修改原先傳入的指標的存儲值,使他指向新的物件。不過我疑惑的是,為什麼修改的是m所指向的值,o卻也跟著改變,這個我尚未明白,請高手指點明燈。

2013年5月31日 星期五

Objective-C if, switch and conditional operator

程式中最常做的事情莫過於判斷流程了,什麼條件下做什麼事。
Objective-C中,有這三個Statement:

if 

if (true){
    do something;
}

當中假設有很多條件判斷時,使用 && (AND)    || (OR) 作複合判斷,如果判斷有優先順序,用小括弧刮起來。

if (temperature > 0 && temperature < 100)
    NSLog(@"目前狀態:液體")
if (temperature < 0 || temperature > 100)  
    NSLog(@"目前狀態不是液體")

條件如果不成立時,可以用else

if (條件判斷){
    真
}else{
    非
}

如果要做的事情只有一行,可以把大括號去掉,而 if 中還可以有 if

if (條件判斷一)
    statement
else
    if (條件判斷二)
        statement
    else
        statement

複雜一點,有可能變這樣


if (條件判斷一)
    statement1
else
    if (條件判斷二)
        statement2
    else
        if (條件判斷三)
            statement3
        else
            statement4


這樣一直縮排下去,可讀性越來越差,你可以改成這樣,效果一樣


if (條件判斷一)
    statement1
else if (條件判斷二)
    statement2
else if (條件判斷三)
    statement3
else
    statement4


switch

當情況變成上述那樣時,就可以考慮用switch。

switch ( value)
{
     case value1:
         statement_A
         .......
         break;

     case value2:
         statement_B
         .......
         break;

     case value3:
         statement_C
         .......
         break;

     case value4:
         statement_D
         .......
         break;

     default:
         statement_Z
         .......
         break;
}

要注意的是value值只能用可以轉成int的型別,如果你要判斷的值無法轉時,試試看Enum。

enum Month{JAN=1,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC};

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        
        enum Month thisMonth = OCT;
        
        switch (thisMonth) {
            case JAN:
                NSLog(@"一月");
                break;
            case FEB:
                NSLog(@"二月");
                break;
            case MAR:
                NSLog(@"三月");
                break;
            case APR:
                NSLog(@"四月");
                break;
            case MAY:
                NSLog(@"五月");
                break;
            case JUN:
                NSLog(@"六月");
                break;
            case JUL:
                NSLog(@"七月");
                break;
            case AUG:
                NSLog(@"八月");
                break;
            case SEP:
                NSLog(@"九月");
                break;
            case OCT:
                NSLog(@"十月");
                break;
            case NOV:
                NSLog(@"十一月");
                break;
            case DEC:
                NSLog(@"十二月");
                break;
            default:
                break;
        }
        
    }
    return 0;
}

Conditional operator

condition ? expression1 : expression2

三元運算子用的好可以讓程式更簡潔,上面的condition 為True時,執行expression1,為False,則執行expression2,看個例子。

NSLog(@"%@", boolVar ? @"True" : @"False" );

可以快速列印出booVar 的值。

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)


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,有興趣的人可以看看。