2011年2月19日土曜日

プロビジョニングプロファイル

プロビジョニング等セキュリティ関連の情報をなんとなくまとめてみました。

ID/プロファイル内容
Bundle IDアプリケーション固有のID。アプリケーション1つに1個。(例:com.apple.iTunes)
App ID開発者がアプリケーションに「Push Notification」「in App Purchase」「Game Center」等へのアクセスを許可・禁止設定する為の挙動を管理するID。
キーチェインでのアクセス識別子となり、それぞれのアプリケーションの挙動を制御する。
アプリケーションの「挙動」について1つ付けられるIDの為、複数アプリケーションをまとめて指定できるワイルドカード指定が認められている。(例:com.apple.*)
ただし、GameCenter等のサービスを許可する時はワイルドカード指定は使えない。つまり各アプリケーションに1個のApp IDを付けなければならない。
プロビジョニングプロファイルデバイスにインストールされて、実行時に正規のルートからインストールされたか確認するための情報を保持するファイル。(App ID/デバイスID/開発者IDを保持)

2011年2月4日金曜日

メモリリークしない為に その1

NSMutableArrayやNSArrayをautorelease以外でメモリ確保した場合、配列を解放しても要素の解放処理が呼び出されるわけではありません。


ar1 = [[NSMutableArray alloc] initWithObjects:@"TEST1", @"TEST2", @"TEST3", nil];
ar2 = [[NSMutableArray alloc] initWithObjects:[SomeClass alloc]
, [SomeClass alloc]
, [SomeClass alloc], nil];

arrays = [[NSArray alloc] initWithObjects:ar1, ar2, nil];
・
・
・
[arrays release]; // ar1 / ar2 とSomeClassがメモリリーク


上記例では、「arrays」の解放しか行っていないので、「ar1」「ar2」「SomeClass」が全てメモリリークとなります。
「TEST1」「TEST2」「TEST3」の文字列はautoreleaseされるので解放の必要はありません。

要素を全て解放すればメモリリークしなくなります。

[ar1 release]; // ar1を解放
// ar2の要素を解放
    for (int i = 0; i < ar2.count; i++) {
    SomeClass* tmp = [ar2 objectAtIndex:i];
    [tmp release];
 }
[ar2 release]; // ar2を解放 [arrays release]; // arraysを解放

2011年1月18日火曜日

UINavigationBar

UINavigationBarをUINavigationControllerではなく、UIViewController等に独自に実装する場合、以下のようにします。


// ナビゲーションバーを生成
UINavigationBar* navBarTop = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, 320, 40)];
navBarTop.alpha = 0.7f;

// ナビゲーションアイテムを生成
UINavigationItem* title = [[UINavigationItem alloc] initWithTitle:@"Title"];

// 戻るボタンを生成
UIBarButtonItem* btnItemBack = [[UIBarButtonItem alloc] initWithTitle:@"Back" style:UIBarButtonItemStyleBordered target:self action:@selector(clickBack:)];

// ナビゲーションアイテムの右側に戻るボタンを設置
title.rightBarButtonItem = btnItemBack;

// ナビゲーションバーにナビゲーションアイテムを設置
[navBarTop pushNavigationItem:title animated:YES];

// ビューにナビゲーションアイテムを設置
[self.view addSubview:navBarTop];


ナビゲーションアイテムを生成するときにタイトルを設定します。上記では「Title」という文字列を設定しました。これで画面上部に大きな文字で「Title」と表示されます。

また、ナビゲーションアイテムにiPhoneアプリでよく見られる、画面右上と左上に表示されるボタンなどを設置します。
上記では右上に「戻る」ボタンを追加していますが、leftBarButtonItemプロパティに設置すれば、左上にも表示できます。

UIBarButtonItemはカスタマイズ可能で、例えば

// segmentedControlはUISegementedControlのインスタンス
UINavigationItem* title = [[UINavigationItem alloc] initWithCustomView:segmentedControl];

等とすれば、ナビゲーションバーの内部にUISegmentedControlのインスタンスや、その他スライダー等、いろいろと設置することが可能なようです。

最後に「pushNavigationItem」でナビゲーションバーにアイテムを設置します。
「addSubview」を使いそうになりますので注意が必要です。

2011年1月9日日曜日

連続アニメーション

複数のアニメーションを連続して再生させるには、1つのアニメーションの終了を検知し、その後、続きのアニメーションを再生させます。

アニメーションの終了は「setAnimationDidStopSelector:finished:context:」でアニメーションの終了メソッドを登録しておきます。


[UIView beginAnimations:@"Animation1" context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(animationFinished:finished:context:)];
// imgViewはUIImageViewのインスタンス
imgView.frame = CGRectMake(100, 200, imgView.frame.size.width, imgView.frame.size.height);
[UIView commitAnimations];
・
・
・
- (void)animationFinished:(NSString *)animationID finished:(BOOL)finished context:(void *)context
{
if ([animationID isEqualToString:@"Animation1"]) {
[UIView beginAnimations:@"Animation2" context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
imgView.frame = CGRectMake(100, 50, imgView.frame.size.width, imgView.frame.size.height);
[UIView commitAnimations];


上記例では、イメージ(imgView)を1秒間かけて(100, 200)に移動した後、続いて0.5秒間かけて(100, 50)に移動させます。

2010年12月10日金曜日

変更される可能性のあるクラスのメソッドを呼び出す

任意のクラスのメソッドを呼び出すには下記のように

[testClass testMethod];

等と呼び出せば良いですが、例えば上記の「testClass」がプログラム実行中に動的に変更されてしまう場合は以下のようにします。


objc_object* ptrClass = testClass;
SEL ptrMethod = @selector(testMethod:);
[ptrClass performSelector:ptrMethod withObject:パラメータ]; // 呼び出し
・
・
ptrClass = testClass2; // 動的にクラスを変更
ptrMethod = @selector(testMethod2:); // 動的にメソッドを変更
[ptrClass performSelector:ptrMethod withObject:パラメータ]; // 呼び出し


C++に慣れた方であればポインタ変数を任意のクラスにキャストしてメソッド呼び出しを行いそうですが、Objective-Cでは上記のようにします。

なお、withObjectでパラメータを複数渡したい場合は、複数オブジェクトを格納するNSArrayオブジェクトを渡すようにします。
また、afterDelay:で秒数を指定すれば、任意の秒数後にメソッド呼び出しが行われます。

NULLとnil

NULLとnilはどちらも値としては同じ0のようです。

しかし、NULLはポインタに対して空という意味であり、
nilはオブジェクトに対して空という意味を表すそうです。

CやC++言語でBOOL型に0かFALSEを使いましたが、それと似たような感じです。
現状ではNULLでもnilでもどちらでも良さそうですが、今後問題とならないように、きちんと使い分けをしておいた方が良いかもしれません。

ところで、NSMutableArrayにnilを追加することはできないようですが、NSNullという空の意味を表すオブジェクトが用意されており、これを使えば追加することができるそうです。

2010年12月8日水曜日

独自のUIViewを作る

独自のUIViewを作るには、描画処理を独自に作成する必要があります。

UIViewでは描画を行う時に「drawRect:(CGRect)rect」メソッドが呼び出されます。

このメソッドをオーバーライドして独自の描画処理を行わせます。

独自のUIViewを作成するのは、割と低レベル(OSに近くなると言う意味)なことですので、C言語の関数で描画を行う必要があります。また、フォントの扱い等、多少クセがあるようです。


-(void)drawRect:(CGRect)rect
{
// 描画範囲を取得
CGRect parentViewBounds = self.bounds;

// ビューのコンテクストを取得
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 塗りつぶし色設定
CGContextSetFillColorWithColor(ctx, [UIColor whiteColor].CGColor);

// 塗りつぶし色でビューを塗りつぶし
CGContextFillRect(ctx, CGRectMake(0, 0, parentViewBounds.size.width, parentViewBounds.size.height));

// 描画設定終了
CGContextClosePath(ctx);

// パスを描画
CGContextStrokePath(ctx);
}

UIViewのboundプロパティは、自身のUIViewの描画範囲をCGRect構造体で取得するプロパティです。

UIGraphicsGetCurrentContext()関数は、自身のUIViewインスタンスのコンテキストを取得する関数です。後の描画関数で引数として使用されます。

上記では、色の設定関数(CGContextSetFillColorWithColor)や塗りつぶし関数(CGContextFillRect)を呼び出していますが、実際の描画は最後に呼び出している描画関数(CGContextStrokePath)で行われます。色やパスの設定関数を呼び出した時点ではないことに少し注意が必要です。
例えば、色を何回変更してパスの設定を行ったとしても、実際の変更が行われるのはCGContextStrokePath等の描画関数を呼び出した時点ですので、途中の色設定は無視されて、最後の色設定のみが反映されることになります。

また、マウス操作などでリアルタイムに描画を変更したい場合などは、「setNeedsDisplayInRect」メソッドを呼び出せば、いつでもdrawRectメソッドがCallbackされます。

[self setNeedsDisplayInRect:self.frame];



描画を行う関数は大量にありますが、ほんの一部を抜き出すと、以下のようになります。
もっと知りたい方は、ドキュメントをCGContextで検索すればたくさん出てきます。

関数概要
CGContextAddLines線の描画設定
CGContextAddPathパスの描画設定
CGContextSetStrokeColorWithColor線の色設定
CGContextSetLineWidth線の太さ設定
CGContextSetFillColor塗りつぶし色設定
CGContextShowTextAtPointテキストの設定
CGContextSelectFontフォントの設定

上記表は描画関数のほんの一部です。上記表にはありませんが、イメージを描画したり、カーブ線を描画する関数もあるようです。

2010年12月1日水曜日

経過時間を取得する

経過時間を取得するには、以下のようにします。


NSDate* startTime = [[NSDate alloc] init];
・
・
・
NSTimeInterval time = [[NSDate date] timeIntervalSinceDate:startTime];

timeに経過秒数が格納されます。

2010年11月25日木曜日

UIViewの追加・削除

UIViewにUIViewを追加するには、addSubviewメソッドを使用します。
addSubviewにはUIViewクラスか、UIViewを継承したクラスのインスタンスが指定できます。


[self.view addSubview:UIViewクラスを継承したクラスのインスタンス];

上記でself.viewに指定したUIViewが追加されます。

一度addしたUIViewを削除するには、removeFromSuperviewメソッドを使用します。

[UIViewクラスを継承したクラスのインスタンス removeFromSuperview];

上記で指定したインスタンスの解放まで行ってくれますので、releaseは不要です。


2010年11月11日木曜日

スレッド

IOS4からiPhoneでもスレッドが使えるようになりました。
スレッド呼び出しは、UIViewの「performSelectorInBackground」メソッドを使用します。

performSelectorInBackground:スレッドのセレクタ withObject:オブジェクト

例えば、ボタンがクリックされたら別スレッドで処理を走らせるには、以下のようにします。

-(IBAction) clickBtnStart:(UIButton*)sender
{
    // ラベルに別スレッドが走ることを表示
    label.text = @"Running background thread";

    // UIActivityIndicatorでwaitアニメーションさせる
    [activityIndicator startAnimating];


    // 別スレッドを呼び出し
    [self performSelectorInBackground:@selector(backgroundThread:) withObject:nil];
}

-(void) backgroundThread:(NSObject*)param
{
    NSAutoreleasePool* pool;
    pool = [[NSAutoreleasePool alloc] init];
   
    [NSThread sleepForTimeInterval:10.0];  // wait 10sec
   
    [activityIndicator stopAnimating];
    label.text = @"Finished";
    [pool release];
}

上の例は、ボタンがクリックされたらラベルに「Running background thread」と表示し、ActivityIndicatorでWaitのアニメーションをさせます。

backgroundThreadが別スレッドで動作する処理です。
「NSAutoreleasePool」というのは、インスタンス確保されたメモリを自動破棄する仕組みです。
NSAutoreleasePoolの生成からreleaseまでの間にインスタンスの確保が行われた場合、releaseでそれまで生成されたインスタンスを一括解放してくれるというものです。
ただ、自動で解放するという仕組みがなかなかくせ者で、思わぬクラスで自動解放されてしまったりすることがあるので注意が必要です。
上記の例ではインスタンス生成を行っていないので無意味ですが、忘れないようにとりあえず書いています。

NSThreadクラスの「sleepForTimeInterval」メソッドで10秒ウェイトさせた後、スレッドの処理が終了したことをラベルとActivityIndicatorのアニメーションをストップさせることで通知させています。

上記のようにすると、バックグランドスレッドで重い処理を行わせていても、メインスレッドにほとんど影響がないことが分かります。