カスタム検索
ラベル Objective-C の投稿を表示しています。 すべての投稿を表示
ラベル Objective-C の投稿を表示しています。 すべての投稿を表示

2009年1月14日水曜日

iPhoneでJsonをごにょごにょ

簡単なJSONを返すアクションを作成

def json = {
def jsonData=[["name":"任天堂","hard":"wii"],
["name":"sony","hard":"PlayStation3"],
["name":"Microsoft","hard":"XBOX360"]]

render(text:jsonData as JSON)
}


ブラウザで確認すると、こんな感じのJSONデータが返ります。

[{"name":"任天堂","hard":"wii"},{"name":"sony","hard":"PlayStation3"},{"name":"Microsoft","hard":"XBOX360"}]


これをiPhoneでうけとってTableViewに表示します。
JSONを取得する部分

- (void)getJSON {
NSURL *jsonURL = [NSURL URLWithString:@"http://localhost:8080/jsonSample/test/json"];
NSMutableString *jsonData = [NSMutableString stringWithContentsOfURL:jsonURL encoding:NSUTF8StringEncoding error:nil];
[jsonData replaceOccurrencesOfString:@"'"
withString:@"\""
options:NSCaseInsensitiveSearch
range:NSMakeRange(0,[jsonData length])];

NSLog(jsonData);

if (jsonData == nil) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Webservice Error" message:@"JSON Dataの取得に失敗しました。" delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alert show];
[alert release];
} else {
SBJSON *json = [SBJSON new];
json.humanReadable = YES;

id result = [jsonData JSONValue];
NSLog([json stringWithObject:result error:NULL]);

webResult = [[NSMutableArray alloc] init];

NSEnumerator* enumerator;
enumerator = [result objectEnumerator];

// while文を使って要素にアクセスする
id obj;
while (obj = [enumerator nextObject]) {
[webResult addObject:obj];
}
printf("データ件数 %d",[webResult count]);
}
}


基本は前と一緒です。シングルクォートをダブルクォートに置換したりしてます。
webResultはクラスのフィールドです。
取得した値をループし、webResultへ追加していきます。

InterfaceBuilderでTableViewをおいて、Outletとdelegateを設定します。



dataSource、delegateをFile'sOwnerに設定し、クラスに定義したtableViewと連結します。
クラスにtableView用の定義をします。

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [webResult count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell;
cell = [[UITableViewCell alloc] init];
cell.text = [[webResult objectAtIndex:indexPath.row] objectForKey:@"name"];
return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"あなたが選んだのは"
message:[[webResult objectAtIndex:indexPath.row] objectForKey:@"hard"]
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles: nil];
[alert show];
[alert release];
}


上から順番に
numberOfRowsInSection : テーブルに何件あるかを返すメソッド
cellForRowAtIndexPath : 1行毎に表示するメソッド
didSelectRowAtIndexPath : 行を選択した時のアクション
です。

詳細はメモなんで略 :p

2009年1月12日月曜日

よろしい、ならばiPhoneでJSONだ その3

Grailsのアプリと通信してJSONデータを取得するサンプルです。

Grails側にJSONデータを返すアクションを用意します。

def json = {
def jsonData=["任天堂":"wii","sony":"PlayStation3","Microsoft":"XBOX360"]

render(text:jsonData as JSON,contentType:"application/x-json")
}

contentTypeは application/json でも無指定でも読み込める。

Objective-C側

NSURL *jsonURL = [NSURL URLWithString:@"http://localhost:8080/jsonSample/test/json"];
NSString *jsonData = [[NSString alloc] initWithContentsOfURL:jsonURL encoding:NSUTF8StringEncoding error:nil];

if (jsonData == nil) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Webservice Error" message:@"JSON Dataの取得に失敗しました。" delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alert show];
[alert release];
} else {
SBJSON *json = [SBJSON new];
json.humanReadable = YES;

id jsonItem = [jsonData JSONValue];
NSLog([json stringWithObject:jsonItem error:NULL]);
}

NSString の initWithContentsOfURL で encoding を指定するのがポイントです。
encodingがあってないと nil になって通信出来てないのかと思って悩んでました・・・

そろそろインターフェース側の処理をやっていこうかな :)

よろしい、ならばiPhoneでJSONだ その2

Objective-CでJsonを取り扱うスニペットめもりんぐ

NSDictionaryとNSStringをいったりきたり


// NSDictionaryからStringに
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
@"The iPhone", @"Today",
@"The World", @"Tomorrow",
nil];
SBJSON *json = [SBJSON new];
json.humanReadable = YES;
NSLog([json stringWithObject:dict error:NULL]);

// StringからNSDictionaryに
NSString *jsonString = @"{\"hoge\":\"foo\",\"wow\":[\"good\",\"cool\"]}";
id jsonObj = [json objectWithString:jsonString error:NULL];
if(jsonObj==NULL || jsonObj==nil) {
printf("NULL dayo\n");
} else {
NSLog([json stringWithObject:jsonObj error:NULL]);
}


jsonでパース出来るのは、ダブルクォートだけです、シングルコートはだめです。(これにずっと気づかなかった・・・)
objectWithStringの返り値はNSArrayだったりNSDictionaryだったりするので、idで受け取る必要があります。

2009年1月6日火曜日

よろしい、ならばiPhoneでJSONだ

GoogleCodeにてObjective-CのJSON Frameworkが公開されています。
http://code.google.com/p/json-framework/

これを使ってiPhoneでJSONを使用してみましょう。
以下の記事を参考にしています。
http://iphone.zcentric.com/2008/08/28/using-threads-for-a-json-request/

まずはJSON Frameworkのインストールです。
GoogleCodeからdmgをダウンロードします。
ユーザのLibraryフォルダへSDKsフォルダをコピーします。
(ユーザが hoge だった場合 > /Users/hoge/Library に dmg内の SDKs をコピー)

xcodeを立ち上げて適当な iPhone プロジェクトを生成します。
メニューから、「プロジェクト」>「プロジェクト設定を編集」 で 追加SDK へ以下の設定を追記します。

$HOME/Library/SDKs/JSON/$(PLATFORM_NAME).sdk



「他のリンカフラグ」 へ以下の設定を追記します。

-ObjC -ljson

あとはJSONを使用するコードで JSON/JSON.h をインポートしてやればOKです。

上記サイトにサンプルアプリもアップされています。
以下サンプルアプリを実行した画面です。

2008年12月31日水曜日

iPhoneで動かすHelloWorld その4

インフルエンザで死んでおります・・・胃が痛い(T_T)
高熱でちょっと忘れかけておりますが、日々の積み重ねが大事です!

ではViewControllerの実装です。

MyViewController.m で警告が出ているはずです。
MyViewController.h で property 指定した textField , label , string に対するメソッドがないという事なので、アクセサメソッドの実装をしてやります。


@synthesize textField;
@synthesize label;
@synthesize string;


これらのインスタンス変数が正しく解放されるように dealloc に追記します。


- (void)dealloc {
[textField release];
[label release];
[string release];
[super dealloc];
}


ボタンを押させると changeGreeting がコールされる所まで確認しましたので、ここでテキストフィールドから文字列を取得し、ラベルを書き換えるようにします。


- (IBAction)changeGreeting:(id)sender {
self.string=textField.text; // textFieldから文字列を取得し、自身のstring変数に格納します。

NSString *nameString=string;
if([nameString length]==0) {
nameString=@"World"; // textFieldの文字列が空なら World を代入する
}
NSString *greeting=[[NSString alloc] initWithFormat:@"Hello, %@!",nameString]; // ラベルに表示するメッセージを作成
label.text=greeting;
[greeting release];
}


ここまででビルドすると、以下のような結果が得られます。



大筋は完了しましたが、キーボードが閉じられない事に気がつくでしょう。
MyViewController.m に以下のコードを追記します。


- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {
if(theTextField==textField) {
[textField resignFirstResponder];
}
return YES;
}


再度実行すると、Doneでキーボードが隠れる事を確認できます。

以上で完成です!思ったより長かったです・・・体が痛い(T_T)

2008年12月27日土曜日

iPhoneで動かすHelloWorld その3

Viewに物をおいてみましょう!

ControllerView.xib をインターフェースビルダーで起動して、LibraryからTextField と Label と Round Rect Button を配置します。
↓こんな感じです。



各Objectの設定はInspectorパネルから色々操作出来ます。
TextのInspectorを見ると、TextInputTraitsという項目があって、ここでキーボードの形や、決定ボタンの形などが調整出来ます。

ここでシミュレーターで実行してみると、キーボードは開きますが、消す事が出来ない状態です。
これはキーボードの終了イベントを受け取る場所がないのでキーボードが閉じられないというわけです。

このイベントをMyViewControllerと関連付けします。
関連付けはアウトレットというインスタンス変数によって行われます。

MyViewController.h

#import

//MyViewControllerはテキストフィールドのデリゲートになるため、UITextFieldDelegateプロトコルを採用する必要があります。
//プロトコルの指定はクラスの継承元クラスの後に <> で指定します。
@interface MyViewController : UIViewController {

//IBOutletは、続く宣言をアウトレットとして使う。という指示です。
IBOutlet UITextField *textField;
IBOutlet UILabel *label;
NSString *string;
}

//@propertyでgetter,setterをproperty定義します。
@property (nonatomic, retain) UITextField *textField;
@property (nonatomic, retain) UILabel *label;
@property (nonatomic, copy) NSString *string;

//Helloボタンを処理するchangeGreetingメソッドを定義します。
//IBActionは、続く宣言をターゲット/アクション接続のアクションとして扱う。という指示です。
-(IBAction)changeGreeting:(id)sender;

@end


実装クラスへメソッドの中身を定義します。

MyViewController.m

- (IBAction)changeGreeting:(id)sender {
printf("call!");
}


保存してインターフェースビルダーへ戻り、File's OwnerのIdentity Inspectorをみると、
Class ActionとClass OutletsにchangeGreetingとlabel,textFieldが追加されています。
これはMyViewControllerクラスがControllerViewのビュークラスとして指定されている為です。

インターフェース上に設置されたオブジェクトとフィールドを関連付けます。
File's Ownerを右クリックして出てきたリストから○をドラッグして、関連付けたいオブジェクトへドロップします。

ボタンのアクションをファンクションへ関連付けます。
ボタンをCTRL+クリックで出てくるInspectorの中からTouch Up Insideの○をFile's Ownerへドラッグ&ドロップします。
changeGreetingが表示されるのでそれを選択する事で関連付けが行われます。

テキストフィールドのデリゲートを設定します。
テキストフォールドをCTRL+ドラッグでFile's Ownerへドロップします。
選択肢からDelegateを選ぶ事でデリゲートの設定が行われます。

ここまで設定したら一度実行してみます。
ボタンを押してコンソールにcall!が表示されたら連携がうまくいっています!
次は実際に処理を書いていきます。(まだまだキーボードは戻せません :p)

2008年12月25日木曜日

iPhoneで動かすHelloWorld その2

Viewを追加するにはViewControllerとnibファイルを使用します。

まずはViewControllerの作成です。
Xcodeから新規ファイルで CocoaTouchClasses の UIViewControler subclass を選択します。
テンプレートを元にざっくりとファイルが生成されます。

作成したクラスをアプリケーションデリゲートのインスタンス変数に追加します。

作成したクラスをMyViewControllerとし、HelloWorldAppDelegateへ以下のように定義します。

HelloWorldAppDelegate.h

#import
@class MyViewController; // クラス宣言

@interface HelloWorldAppDelegate : NSObject {
UIWindow *window;
MyViewController *myViewController; // 追加したビューをインスタンス変数として定義します
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) MyViewController *myViewController;

@end


HelloWorldAppDelegate.m

#import "HelloWorldAppDelegate.h"
#import "MyViewController.h" // インポート宣言

@implementation HelloWorldAppDelegate

@synthesize window;
// アクセサメソッドの合成?
@synthesize myViewController;

- (void)applicationDidFinishLaunching:(UIApplication *)application {
// ViewControllerの作成、ControllerView.nibをターゲットとします。bundleはresouceの位置的な情報?
MyViewController *aViewController = [[MyViewController alloc]initWithNibName:@"ControllerView" bundle:[NSBundle mainBundle]];
// デリゲータのmyViewControllerに上で作成したインスタンスを指定
self.myViewController=aViewController;
// 一時インスタンスの解放
[aViewController release];

// myViewControllerからviewを取得
UIView *controllersView=[myViewController view];
// windowへControllerViewのviewを追加
[window addSubview:controllersView];

[window makeKeyAndVisible];
}

- (void)dealloc {
[myViewController release]; // デリゲートが解放される時(アプリ終了時)にビューを一緒に解放します
[window release];
[super dealloc];
}

@end


ビューを作成します。
InterfaceBuilderを起動して新規作成>CocoaTouch>Viewでビューが作成できます。
ControllerView.nib としてHelloWorld プロジェクトに保存します。
Xcodeが立ち上がっていれば、自動的にリソースとして取り込むか選択が出ます。
リソースとして登録されたらResourcesフォルダに移動します。

このViewがどのコントローラークラスに所属するかを指定します。
Tools > Identity Inspector を起動して、File's Ownerを選択>ClassにMyViewControllerを指定します。
File's Ownerにアウトレット(nibファイル内の項目に接続しているインスタンス変数)を設定します。
File's OwnerをCtrlクリックしながらViewまでドラッグすればOKです。

Tool > Attribute Inspector から BackgroundColorを変更してシミュレーターで動かすと、このViewが正しく読み込まれているのがわかります。

次はビューにオブジェクトを追加します。

2008年12月23日火曜日

iPhoneで動かすHelloWorld その1

とりあえずHelloWorld的なものを作ってみよう!
とはいってもDeveloperCenterのiPhoneアプリケーションチュートリアルを実行しながらメモるだけですが :)

まずは最低限必要なCocoaのデザインパターンの説明をざっくりと

・Delegation
オブジェクトが、デリゲートとして指定された別のオブジェジェクトに定期的にメッセージを送信し、入力の要求やイベントの発生を通知するデザインパターン。
このデザインパターンはクラス継承の代替手段として、再利用可能なオブジェクトの機能を拡張するために使用する。

使用される場所:メイン処理の終了感知、ビューのセットアップ&管理、テキストフィールドのReturnイベント処理等

・Model-View-Controller
いわゆるMVC。
Model=データ、オブジェクト
View=ユーザインターフェース部分
Controller=ModelとViewを仲介する部分

・Target-Action
コントロールオブジェクトから、別のオブジェクトにメッセージの送信と、送信されたメッセージを受け取って処理する仕組み。

使用される場所:ボタン押す>Controllerへ指示


次に、Xcodeを起動して新規プロジェクトの作成を選択。
Windows-Based Applicationを選択。
とりあえず「ビルドして進行」するとiPhoneシミュレーターが立ち上がって白い画面が出たらOK

まずはどのようにアプリケーションが起動して画面が出て行くのかを追ってみよう!
main.m をみるとUIApplicationMainを起動しているだけなのがわかりました。
UIApplicationMainは info.plist 内の MainNibFile 値のインターフェースを起動します。
デフォではMainWindowになっているので、MainWIndow.xibが読み込まれます。
(前回はここの関連が?だった)

MainWindowをダブルクリックするとインターフェースビルダーが起動します。
そこに含まれる4つの項目は以下のような意味をもっています。
・File'sOwner
 UIApplicationのインスタンスです。
・First Responder
 イベント処理、今はまだ使用しません。
・[アプリケーション名]AppDelegate
 アプリケーションのデリゲートインスタンス
・WIndow
 実際の画面に表示されるウインド

アプリケーションは起動が完了すると applicationDidFinishLaunching をデリゲートに送信します。
デフォルトで作成されるこのメソッド処理を見てみます。

- (void)applicationDidFinishLaunching:(UIApplication *)application {

// Override point for customization after application launch
[window makeKeyAndVisible];
}

windowを定義してるだけですね。

次回はここへ、デフォルト以外のViewを追加してみます。

2008年12月21日日曜日

Objective-Cにおけるメモリ管理なんて怖くない!その2

前回、retain , release と dealloc でインスタンスのメモリ管理をやりました。
deallocで関連するインスタンスの解放をするのは良いとしても、 release 自体も忘れてしまったりする事があります。

Objective-Cでは autorelease という仕組みがあるそうなのでそこを見ていきます。
NSAutoreleasePool クラスを使って実現します。
NSAutoreleasePool は、自動解放されるべきインスタンスを保持しておくクラスです。
クラスが定義されると、 autorelease対象の広場(プール)が作成されます。
広場が存在する状態で、他クラスのインスタンスに autorelease を送ると、そのインスタンスは広場にほうりこまれます。
(広場に存在するだけで参照されているわけではないので、インスタンスのリファレンスカウンタは変わらない)

広場の release を実行すると、広場に存在するインスタンスすべてに release が実行されます。


id pool=[[NSAutoreleasePool alloc] init]; // 広場の作成

id foo=[[Foo alloc] init]; // Fooクラスのインスタンス作成。foo のリファレンスカウンタは1
[foo autorelease]; // foo インスタンスを広場に登録する。リファレンスカウンタは1のまま

[pool release]; // foo にもreleaseが実行される


NSAutoreleasePool は、複数存在してもよい。
複数存在する場合に autorelease を実行した場合は、一番近い所で指定されたプールへほうりこまれる。
基本は開始と終了でメインの pool を作り、内部でループなど一時的なインスタンスを作成する場所で子プールを作成する。


id mainPool=[[NSAutoreleasePool alloc] init];

id hoge=[[Hoge alloc] init];
[hoge autorelease]; // mainPool へ登録される

while(条件) {
id subPool=[[NSAutoreleasePool alloc] init];

id foo=[[Foo alloc] init];
[foo autorelease]; // subPool へ登録される

[subPool release];
}

[mainPool release];


CocoaのGUIアプリケーションはNSApplicationがイベント発生時(マウスクリック等)に子プールを作成して、イベント終了時にreleaseしてくれるので、それほど気にしなくても自動でやってくれる。
iPhoneSDKもそうなのかな?


・オーナーシップとメモリ管理
init~~ で呼ばれるメソッドはオーナーシップが定義される。(メモリの管理をする必要がある)
同等のメソッドで init がない場合は一時的なインスタンスとなり、 autorelease に登録される。

例:文字列を扱うNSStringクラスの場合

-(id)initWithUTF8String:(const char *)bytes
+(id)stringWithUTF8String:(const char *)bytes

どちらも文字列を保持するオブジェクトが作成されるが、initは生成したオブジェクトがオーナーとなり、
stringはプールへ登録される(autoreleaseが実行される)

オーナーシップを与えないコンストラクタをコンビニエンスコンストラクタと呼ぶ。

・シングルトンの定義
定数やシングルトンを作成する為には、retain,release,retainCountの機能を上書きする必要がある。
retainとreleaseを何もしないメソッドにし、retainCountは常に UINT_MAX を返すようにする。

2008年12月20日土曜日

Objective-Cにおけるメモリ管理なんて怖くない!

Object-C2.0でのメモリ管理には2種類の方法がある。

・リファレンスカウンタ方式
・ガーベジコレクション

ガーベジコレクションは2.0から採用された自動メモリ管理の仕組みで、Javaのガーベジコレクションと同じ要領である。
一定時間毎に不必要な(参照されていない)クラスを探しだし、解放していく。
これは後にとっておくとして・・・

まずはリファレンスカウンタ方式から詳しく見ていく。
一言でいうと、クラスがいくつ参照されているかをカウンタで管理し、0になったらメモリを解放するという仕組みである。

具体的には alloc init された時点で、インスタンスはカウンタを1とする。

以下例:

id foo = [[Foo alloc] init]; // fooインスタンにカウント1が刻まれる
[foo release]; // fooインスタンスのカウントは0となり、メモリがリリースされる

id foo2 = [[Foo alloc] init]; // foo2インスタンスにカウント1が刻まれる
id hoge = [[Hoge alloc] init];
[hoge setFoo:[foo2 retain]]; // HogeインスタンスにはsetFooでfooを参照する機能がある。この時点でfoo2インスタンスにカウント2が刻まれる

[foo2 release]; // foo2のカウントが1に減る
[foo2 release]; // foo2のカウントが0となり、メモリがリリースされる


・retain メソッドでカウントを+1する。このメソッドは自身を返す。
・release メソッドでカウントを−1する。
・現在のカウント数は retainCount メソッドで見る事が出来る。
・dealloc でメモリをリリース出来るが、これはカウンタに関係なくリリースされてしまうので使うべきではない。

でもいちいち手動でリリースしてたらきりがない!
そこで dealloc をオーバーライドして、インスタンスが解放される時にインスタンスが保持するオブジェクトをリリースするのだ。

以上をふまえて簡単なテストプログラム

ReferenceCounterクラス

#import

@interface ReferenceCounter : NSObject {
id obj;
}
-(void)setObj:(id)_obj;
@end


@implementation ReferenceCounter
-(void)setObj:(id)_obj {
obj=[_obj retain]; // 何かオブジェクトがセットされる。ただ代入するのではなく、使用するよ!とカウンタを+する。
}

-(void)dealloc {
[obj release]; // 自身がリリースされるまえに、自分がもってるオブジェクトをリリースする。
[super dealloc]; // 自身のリリース
}

@end


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

id foo=[[NSObject alloc] init];
printf("retainCount: %d\n",[foo retainCount]);

id rctest=[[ReferenceCounter alloc] init]; // テストクラスを作成
[rctest setObj:foo]; // テストクラスになんかオブジェクトをセットする
printf("retainCount: %d\n",[foo retainCount]);

[foo release]; // オブジェクトをリリース
printf("retainCount: %d\n",[foo retainCount]);

[rctest release]; // テストクラスをリリース

return 0;
}


・foo インスタンスが alloc されて foo = 1 カウンタがつく
・rctest インスタンスが alloc されて rctest = 1 カウンタがつく
・rctest に foo をセットする。setter内で foo を retain するので foo=2 になる
・foo インスタンスを release する。 foo=1 になる
・rctest をリリースする。rctest=0になり、dealloc がよばれ rctest内のobj(foo)がreleaseされる。 foo=0になる

出力は以下のような感じ
retainCount: 1
retainCount: 2
retainCount: 1

なるほど、オブジェクト思考っぽい。
この考えは普通にJavaでも活かせる気がする!

2008年12月17日水曜日

いまさら聞けない「Objective-C」超入門 Objective-C その4

そんなこんなで第4回。

動的結合ってなんじゃ?
・存在しないメソッドを実行しようとしたコードを書いた場合に、コンパイル時にエラーが出ず、実際に実行したときにメソッドが実行出来るかを確認する方式を「動的結合」と呼ぶ
・Javaは比較的静的結合、Groovyみたいにダイナミックに扱う場合は動的結合、という認識でいいのかな?

Object は id として扱うとしてきたが、普通にクラス名で宣言してもよい

Class Fooがあったとして
id c;
c=[Foo alloc];

Foo *c;
c=[Foo alloc];
どちらでもFooのインスタンスを作成出来る。

クラス名をしておくと、不正なクラス利用をされていた場合にコンパイル時にチェックしてくれる。
ダイナミックにクラスが変わる場合は id を使うって事ね!

nilについて
・nilはポインタが空の状態、NULLとは違う
・Classのinitに失敗した場合等に nilとなる
・JavaでNullになんかすると NullPointerException が出るが、 nil にメッセージを送ってもエラーにならない(ただしプログラム上ではわかりやすくする為に nil 判定を書いておく事は重要)

インターフェース内のクラス指定について
Objectの中に別のClassを持つ場合、 importして書く事も出来るが、通常は @class 宣言を使う。


@class Hoge;

@interface Foo : NSObject {
Hoge *ho;
}


HogeはClassだよと教えてやるわけです。ただし実装部では import する必要があります。

変数hogeにgetter , setter を定義する場合
-(int)hoge;
-(void)setHoge:(int)value;
getterにはgetを付けない!

Class型って?
・インスタンス化されたものじゃなくて、元となるクラス
・NSObjectに用意された class を呼ぶと、そのクラスのClass型を取得できる
これを使って動的にクラスを割り当てるサンプル!

Class theClass = flag ? [Foo class] : [Hoge class];
id v = [theClass alloc];


flagがtrueなら v はFooのインスタンスになり、falseなら v はHogeのインスタンスとなる。

インスタンスのクラスが指定したクラスのインスタンスかをチェックするには isMemberOfClass を使う。
BOOL isFoo = [v isMemberOfClass:[Foo class]];
v がFooのインスタンスであれば isFoo は true となる。

クラスメソッドとは?
・Javaでいうところのstaticメソッド?
・NSObjectには、class とか isMemberOfClass とか、alloc がクラスメソッドとして定義してあるため、上記のように使える
・自分でクラスメソッドを定義するには、今まで - と書いてた所を + と書く
例:allocの定義
+(id)alloc;

クラス内から、自分のクラスメソッドを呼ぶとき等は、
[[Foo alloc] say];
と書いてしまうと、Fooを継承したクラスで不都合が起きてしまうので
[[[self class] alloc] say];
とすれば、必ず呼ばれた時のクラスのクラスメソッドを呼ぶ事が出来る。

今日はこの辺りで・・・、次回はやっとメモリ関係

2008年12月16日火曜日

いまさら聞けない「Objective-C」超入門 Objective-C その3

そんなこんなで継承について

スーパークラスとかサブクラスとか継承の考え方はJavaとまったく一緒、省略
Cocoa環境では基本的にNSObjectというのがルートクラスとなる。JavaでいうObject的なものっぽい。
C++にはこういったすべてのベースとなるルートクラスがないらしい(!)

では実際に以下のような継承クラスを作成してみる
Parent = ルートクラス callNumber(int) で value に値をセットし、受け取った値を返す
Child = Parentのサブクラス、callNumber(int) をオーバーライドし、 受け取った2倍の値を返す
Grandchild = Childのサブクラス、callNumberでセットされた value の3倍の値を返す anotherNumber メソッドを追加

Parent.h

#import

@interface Parent : NSDocument {
int value;
}
- (int)callNumber:(int)num;

@end


Parent.m

#import "Parent.h"

@implementation Parent
- (int)callNumber:(int)num {
value=num;
return num;
}

@end


Child.h

#import
#import "Parent.h"

@interface Child : Parent {
}
-(int)callNumber:(int)num;

@end


Child.m

#import "Child.h"

@implementation Child
-(int)callNumber:(int)num {
value=num;
return num * 2;
}

@end


Grandchild.h

#import
#import "Child.h"

@interface Grandchild : Child {
}
-(int)anotherNumber;

@end


Grandchild.m

#import "Grandchild.h"

@implementation Grandchild
-(int)anotherNumber{
return value * 3;
}
@end


そしてこれらを実行して結果を出力する main.m
せっかくなんで入力された文字のキーコードを出力する q で終了


#import
#import "Parent.h"
#import "Child.h"
#import "Grandchild.h"

int main(void)
{
id p,c,g;
char buf[8];

p=[Parent alloc];
c=[Child alloc];
g=[Grandchild alloc];

while(scanf("%s",buf) > 0) {
switch(buf[0]) {
case 'q':return 0; break;
default:
printf("parent class >> %d\n",[p callNumber:buf[0]]);
printf("child class >> %d\n",[c callNumber:buf[0]]);
[g callNumber:buf[0]];
printf("grandchild class >> %d\n",[g anotherNumber]);
break;
}
}

return 0;
}


今日のポイント
Javaと違う所!
interfaceはクラスには必ず存在し、Javaのように同じインターフェースを持った別のクラスみたいな考えではない
同じような事をやる場合はインターフェース用のクラスを作成する、もちろんクラスだからインターフェースもいる
インターフェース用クラスのインターフェースとかどんだけ〜

なんで import は h ファイルだけでいいのか、昨日の謎がとけた!
早くぐりぐり動かしたいな〜

2008年12月15日月曜日

いまさら聞けない「Objective-C」超入門 Objective-C その2

とりあえず前回動かしたクラスを別々のファイルにする。

インターフェース部を クラス名.h でヘッダファイルとして、実装クラスを クラス名.m とするのが一般的らしい。
クラス名.m には クラス名.h を import する。
別の実装を各場合も クラス名.h を import すればおk。

main.h 内は main だけ残して、Helloクラスを別ファイルにする
main から import するのは クラス名.h だけでおk。何でだろう?同クラス名をデフォの実装として読み込んでくれる?

Hello.h


#import

@interface Hello : NSDocument {
int val;
int min,max,step;
}
- (id)initWithMin:(int)a max:(int)b step:(int)s;
- (int)value;
- (id)up;
- (id)down;
@end



Hello.m


#import "Hello.h"

@implementation Hello
- (id)initWithMin:(int)a max:(int)b step:(int)s {
self=[super init]; // NSObjectの初期化を呼ぶ
if(self!=nil) { // nilはnullみたいなもん? init が成功していたら
val=min=a;
max=b;
step=s;
}
return self;
}

- (int)value {
return val;
}

- (id)up {
if((val += step) > max) val=max;
return self;
}

- (id)down {
if((val -= step) < min) val=min;
return self;
}
@end



main.m


#import
#import "Hello.h"

int main(void)
{
id v,w;

v=[[Hello alloc] initWithMin:0 max:10 step:2];
w=[[Hello alloc] initWithMin:0 max:9 step:3];
[v up];
printf("%d %d\n",[v value], [w value]);
[v up];
[w up];
printf("%d %d\n",[v value], [w value]);

return 0;
}




Objective-CだからといってすべてをObject指向で作る必要はない
Object型より関数型の方が軽いため、あきらかな重い処理は関数型で作成した方が良い
関数定義のみを含むソースは .c ファイルとして扱う
ただしオブジェクトを引数や返り値としたり、メソッド呼び出しなどをする場合は .m とする

#import は複数のクラスを読み込んだ場合、かぶるヘッダファイルを読み込まない
#include は読み込む。(通常はヘッダファイル内で2度読み込まないように定義しておく)
2度読み込みたい事情が無い限り #import でおk

今日はここまで!次回はクラス継承

2008年12月14日日曜日

いまさら聞けない「Objective-C」超入門

というわけでObjective-Cはじめます。初心忘れるべからず。

・はじめに Objective-Cとはなんじゃらほい
 C言語にオブジェクト指向の機能を追加した言語
 MacOSX上で動作する言語、それ以外ではあまり使われない。iPhoneのアプリもObjective-Cで作成する 。
 C++もCにオブジェクト指向を追加した言語だが、Objective-Cとは別路線で互換性は無い
 Objective-Cの特徴としては、際立って柔軟性が高く、効率的なコードを書く事が出来るらしい
 Leopardの登場とあわせてObjective-C2.0(ガベージコレクション、宣言プロパティ、高速列挙を追加)が公開&実装された



なんとなく概要がわかった所でまずは触ってみよう

//オブジェクトは id という特殊な型
id obj

//オブジェクトにメッセージを送る(オブジェクトのメソッドを実行する?)
//この形をメッセージ式と呼ぶ。
[obj msg]

//objのmsgを呼び出した結果が戻り値となる
//以下のように、戻り値を使って別のメッセージを呼ぶ事も出来る
[[obj msg] msg2];
[[[obj msg] msg2] msg3];

// [] は配列としても使う、左側にあるもので自動的に判別される
array=[1,2,3]; // 配列の定義
el=test[[obj msg]]; // test配列の添字として [obj msg] の返り値を使う

//メッセージに引数が必要な場合はコロンを付ける
[obj msg:'yossy']

//メソッド一覧はメッセージセレクタと呼ばれる
//コロンの有無でも別のセレクタとして認識される
//コロンが引数的なキーワード
copy
copy:
delete
allDelete
create
findBookName:Author:

//クラスのインスタンス化
[クラス名 alloc]

//通常はinitするべし
//initがデフォ、initからはじまるメソッドでも良い
[[クラス名 alloc] init]

//インターフェースの作成
@interface Hello:NSObject
{
//インスタンス変数の宣言
}
// メソッドの宣言
- (id)delegate;
- (id)cellAtRow:(int)row column:(int)col;

@end

// this は self と表現される
//文字列を扱うのはちょっと面倒?
//とりあえず入門書に従って数値をいじるクラスが出来た・・・今日はタイムアップなり
// XCodeで新規Cocoaアプリケーション、mainに全部かきかき



#import

@interface Hello : NSDocument {
int val;
int min,max,step;
}
- (id)initWithMin:(int)a max:(int)b step:(int)s;
- (int)value;
- (id)up;
- (id)down;
@end

@implementation Hello
- (id)initWithMin:(int)a max:(int)b step:(int)s {
self=[super init]; // NSObjectの初期化を呼ぶ
if(self!=nil) { // nilはnullみたいなもん? init が成功していたら
val=min=a;
max=b;
step=s;
}
return self;
}

- (int)value {
return val;
}

- (id)up {
if((val += step) > max) val=max;
return self;
}

- (id)down {
if((val -= step) < min) val=min;
return self;
}
@end

//int main(int argc, char *argv[])
int main(void)
{
id v,w;

v=[[Hello alloc] initWithMin:0 max:10 step:2];
w=[[Hello alloc] initWithMin:0 max:9 step:3];
[v up];
printf("%d %d\n",[v value], [w value]);
[v up];
[w up];
printf("%d %d\n",[v value], [w value]);

// return NSApplicationMain(argc, (const char **) argv);
return 0;
}