2013年3月15日金曜日

独自クラスの配列をソートする

独自の型を持つ要素の配列をソートするには、NSArrayクラスの「sortedArrayUsingSelector」メソッドを使います。

例えば、TestClassという独自のクラスを配列にしてソートさせたい場合、以下のようにします。


@interface TestClass : NSObject {
@public
NSString *scale;
}

-(NSComparisonResult)compare_func:(TestClass*)a; // ソート時に呼ばれます

@property (nonatomic, retain) NSString *scale;

@end


@implementation TestClass
@synthesize scale;

-(id)init
{
self = [super init];
if (self) {
scale = @"ちゅうくらい";
}
return self;
}

-(id)initWithString:(NSString*)str
{
self = [super init];
if (self) {
scale = [str retain];
}
return self;
}

-(NSComparisonResult)compare_func:(TestClass*)a // ソート時に呼ばれます
{
if ([self.scale isEqualToString:@"ちゅうくらい"]) {
if ([a.scale isEqualToString:@"おおきい"]) {
return NSOrderedAscending;
} else if ([a.scale isEqualToString:@"ちゅうくらい"]) {
return NSOrderedSame;
} else {
return NSOrderedDescending;
}
} else if ([self.scale isEqualToString:@"おおきい"]) {
return NSOrderedDescending;
} else if ([self.scale isEqualToString:@"ちいさい"]) {
return NSOrderedAscending;
}
return NSOrderedAscending;
}

@end




ソート処理の中身とかは別にどうでも良いのですが、重要なのはソート処理に使うメソッドを用意しておくということです。
このクラスを配列にしてソートさせるには、以下のようにします。


// 独自クラス(TestClass)の配列を生成
NSArray *test_array = [[NSArray arrayWithObjects:
       [[[TestClass alloc] init] autorelease]
     , [[[TestClass alloc] initWithString:@"おおきい"] autorelease]
     , [[[TestClass alloc] initWithString:@"ちいさい"] autorelease]
     , nil] retain];

// ソート
NSArray *sorted_array = [test_array sortedArrayUsingSelector:@selector(compare_func:)];

// 結果を出力
for (TestClass *tc in sorted_array) {
    NSLog(@"%@", tc.scale);
}



NSArrayクラスの「sortedArrayUsingSelector」メソッドの引数に独自クラスに実装したソート用のメソッド(compare_func:)を指定することで、NSArrayがソート時に呼び出してくれるようになります。

結果は以下のようになります。


ちいさい
ちゅうくらい
おおきい






NSArrayクラスの「sortedArrayUsingSelector」メソッドは要素となるクラスにソート処理を実装しなければなりませんが、「sortedArrayUsingFunction」メソッドはソート処理を外部の関数に指定することができます。


// ソート処理を配列要素ではなく、外部の関数に定義
NSInteger compare_func(TestClass *a, TestClass *b, void* context)
{
if ([a.scale isEqualToString:@"ちゅうくらい"]) {
if ([b.scale isEqualToString:@"おおきい"]) {
return NSOrderedAscending;
} else if ([b.scale isEqualToString:@"ちゅうくらい"]) {
return NSOrderedSame;
} else {
return NSOrderedDescending;
}
} else if ([a.scale isEqualToString:@"おおきい"]) {
return NSOrderedDescending;
} else if ([a.scale isEqualToString:@"ちいさい"]) {
return NSOrderedAscending;
}
return NSOrderedAscending;
}


int main (int argc, const char * argv[])
{
NSArray *test_array = [[NSArray arrayWithObjects:[[[TestClass alloc] init] autorelease]
  , [[[TestClass alloc] initWithString:@"おおきい"] autorelease]
  , [[[TestClass alloc] initWithString:@"ちいさい"] autorelease]
  , nil] retain];
NSArray *sorted_array = [test_array sortedArrayUsingFunction:compare_func context:NULL];
for (TestClass *tc in sorted_array) {
NSLog(@"%@", tc.scale);
}
return 0;
}



結果は先の結果と同じになります。




2013年3月14日木曜日

NSArrayのソート

NSArrayをソートするには、sortedArrayUsingComparatorメソッドを使います。

数値を降順にソートする場合


NSArray *num_array = [NSArray arrayWithObjects:[NSNumber numberWithInt:0]
 , [NSNumber numberWithInt:3]
 , [NSNumber numberWithInt:2]
 , [NSNumber numberWithInt:4]
 , [NSNumber numberWithInt:1]
 , [NSNumber numberWithInt:7]
 , [NSNumber numberWithInt:10]
 , [NSNumber numberWithInt:9]
 , [NSNumber numberWithInt:8]
 , [NSNumber numberWithInt:5]
 , nil];
NSArray *sorted_array = [num_array sortedArrayUsingComparator:^NSComparisonResult(NSNumber *a, NSNumber *b) {
return b.intValue - a.intValue; // ソート
}];
for (NSNumber *aNum in sorted_array) {
NSLog(@"sorted_num:%@", aNum);
}


上記のブロック関数内のaとbを入れ替えてやれば昇順にソートさせることもできます。

結果は以下のようになります。


sorted_num:10
sorted_num:9
sorted_num:8
sorted_num:7
sorted_num:5
sorted_num:4
sorted_num:3
sorted_num:2
sorted_num:1
sorted_num:0




文字を降順にソートする場合


NSArray *str_array = [NSArray arrayWithObjects:@"A", @"E", @"B", @"Z", @"C", @"F", @"D", nil];
sorted_array = [str_array sortedArrayUsingComparator:^NSComparisonResult(NSString *a, NSString *b) {
return [b compare:a]; // ソート
}];
for (NSString *str in sorted_array) {
NSLog(@"sorted_str:%@", str);
}


上記のブロック関数内のaとbを入れ替えてやれば昇順にソートさせることもできます。

結果は以下のようになります。


sorted_str:Z
sorted_str:F
sorted_str:E
sorted_str:D
sorted_str:C
sorted_str:B
sorted_str:A



他にもsortedArrayUsingFunctionやsortedArrayUsingSelector等あります。


2013年3月1日金曜日

Cabochaを呼び出す[mac]

(この記事はiphoneではなく、[mac]のみ対応です。)

日本語の構文解析ツールにCabochaというものがあります。
CabochaにはC言語から呼び出す為のAPIが用意されています。
C言語用のAPIですが、もちろんObjective-Cからも呼び出すことが可能です。

Cabochaがインストールされていることが前提ですが(インストール方法は他のサイトでいろいろ出てきますので・・)、その利用方法はまず、コマンドラインから次のコマンドを入力します。

cabocha-config --cflags

上記でCabochaヘッダのインストールパスを取得できます。
私の環境では

-I/usr/local/include

となりました。
ヘッダのインストールパスをxcodeのBuild Settingsの[SearchPaths]の[Header Search Paths]ところに設定します。その際、-Iは不要なので「/usr/local/include」と設定します。

これでCabochaのAPIを利用する準備ができたので、あとは利用するソースからヘッダを引き込む為に

#import "cabocha.h"

を書き、以下のようにすればCabochaが呼び出せます。



const char *p = [@"これは日本語のテストです。" UTF8String];
const char *result;

// Cabocha起動
char *argv[] = {"cabocha", "-f1"};
c = cabocha_new(2, argv);
result = cabocha_sparse_tostr(c, p);

NSString *strResult = [NSString stringWithCString:result encoding:NSUTF8StringEncoding];
cabocha_destroy(c);



cabocha_newの1番めの引数はargvの要素数を設定します。


2013年2月27日水曜日

NSDictionary(plist)をバイナリで保存する

NSDictionaryはwriteToFileで簡単に保存できますが、XMLファイルで保存される為、サイズが非常に大きくなってしまう場合があります。

バイナリファイルで保存するには、NSPropertyListSerializationを使って以下のようにします。


// 辞書を生成
NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];

// 辞書に追加
[dic setValue:@"1" forKey:@"TEST"];

// ストリームを生成
NSOutputStream *outstream = [NSOutputStream outputStreamToFileAtPath:@"./testDic" append:NO];
[outstream open]; // オープン

// 辞書をバイナリでファイルに保存
[NSPropertyListSerialization writePropertyList:dic toStream:outstream format:NSPropertyListBinaryFormat_v1_0 options:NSPropertyListImmutable error:&error];
if (error != nil) {
NSLog(@"Error:%@", error.description);
}
[outstream close]; // クローズ



保存したものを読み込むには以下のようにします。



dic = [NSDictionary dictionaryWithContentsOfFile:@"./testDic"];




NSArrayとNSDictionaryの検索速度

NSArrayとNSDictionaryを生成して検索速度を比較してみました。
結果、NSDictionaryの検索速度の方が速いようです。
ハッシュ(連想配列)なので当たり前と言えば当たり前ですが。。


// 計測用データ作成
NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0 ; i < 500000; i++ ) {
NSString *val = [NSString stringWithFormat:@"%d", i];
[dic setValue:[NSString stringWithFormat:@"%ld", i] forKey:val];
[array addObject:val];
}

NSLog(@"辞書検索Start");
NSDate *startTime = [NSDate date];

NSString *strTest = @"400000";
NSString *num = [dic valueForKey:strTest]; // 辞書を検索

NSTimeInterval interval = [[NSDate date] timeIntervalSinceDate:startTime];
NSLog(@"辞書検索Stop at:%lf", interval);
if (num) {
NSLog(@"There is %@ in the dictionary", strTest);
} else {
NSLog(@"There is not %@ in the dictionary", strTest);
}


NSLog(@"配列検索Start");
startTime = [NSDate date];
unsigned long idx = [array indexOfObject:strTest];

interval = [[NSDate date] timeIntervalSinceDate:startTime];
NSLog(@"配列検索Stop at:%lf", interval);
if (idx != NSNotFound) {
NSLog(@"There is %@ in the dictionary", strTest);
} else {
NSLog(@"There is not %@ in the dictionary", strTest);
}


NSLog(@"配列検索2Start");
startTime = [NSDate date];
BOOL hasString = [array containsObject:strTest];

interval = [[NSDate date] timeIntervalSinceDate:startTime];
NSLog(@"配列検索2Stop at:%lf", interval);
if (hasString) {
NSLog(@"There is %@ in the dictionary", strTest);
} else {
NSLog(@"There is not %@ in the dictionary", strTest);
}


結果は以下のようになりました。(iPhoneでの計測ではなく、Mac上で計測しました。)


辞書検索Start
辞書検索Stop at:0.000017
here is 400000 in the dictionary
配列検索Start
配列検索Stop at:0.036717
here is 400000 in the dictionary
配列検索2Start
配列検索2Stop at:0.037023
here is 400000 in the dictionary


配列検索ではindexOfObjectによる検索とcontainsObjectによる検索を計測しましたが、どちらも大差は無さそうです。
ちなみに、検索する要素を"400000"から"250000"にすると以下のようになりました。




辞書検索Start
辞書検索Stop at:0.000050
There is 250000 in the dictionary
配列検索Start
配列検索Stop at:0.022836
There is 250000 in the dictionary
配列検索2Start
配列検索2Stop at:0.022718
There is 250000 in the dictionary




ハッシュの検索はほぼ大差ない速度(むしろ若干遅くなっている)ですが、配列の検索では要素位置が上のほうにある為か高速に検索が行えました。

結論としては、
・ハッシュ(NSDictionary)は要素の数や位置に関わらずほぼ同一速度で検索が行える。
・配列(NSArray)は要素数や要素位置に応じて検索速度が変わる
・indexOfObjectとcontainsObjectによる検索は速度に大差なし

というところでしょうか。


2012年12月10日月曜日

アドレスブック(連絡先)からの情報取得

アドレスブック(連絡先)へアクセスするには以下の様にします。


// アドレスブックを生成
ABAddressBookRef book = ABAddressBookCreateWithOptions(NULL, nil);
// アドレスブックのレコード配列を取得
CFArrayRef records = ABAddressBookCopyArrayOfAllPeople(book);

// 配列の要素の数だけ繰り返す
for (int i = 0 ; i < CFArrayGetCount(records) ; i++) {
    // 1レコード取得
    ABRecordRef record = CFArrayGetValueAtIndex(records, i);
    // 名を取得
    NSString *firstName = (NSString*)ABRecordCopyValue(record, kABPersonFirstNameProperty);
    if (firstName == nil) {
        firstName = @""; // 無ければ空文字に
    }
    // 氏を取得
    NSString *lastName = (NSString*)ABRecordCopyValue(record, kABPersonLastNameProperty);
    if (lastName == nil) {
        lastName = @""; // 無ければ空文字に
    }
    // 氏名を生成
    NSString *name = [NSString stringWithFormat:@"%@ %@", lastName, firstName];
    NSLog(@"%@", name);
    if (firstName != nil) {
        CFRelease(firstName);
    }
    if (lastName != nil) {
        CFRelease(lastName);
    }
    // レコードから電話番号の取得
    ABMultiValueRef tels = ABRecordCopyValue(record, kABPersonPhoneProperty);
    if (ABMultiValueGetCount(tels)) {
        NSString *tel = (NSString*)ABMultiValueCopyValueAtIndex(tels, 0);
        NSLog(@"%@", tel);
        CFRelease(tel);
    }
}
CFRelease(book);
CFRelease(records);


「ABRecordCopyValue」のパラメータで指定するkABPersonFirstNameProperty等で連絡先から何を取得するかを指定します。
その他プロパティには以下のものがあります。

kABPersonFirstNameファーストネーム
kABPersonLastNameラストネーム
kABPersonMiddleNameミドルネーム
kABPersonPrefixPropertyプレフィックス
kABPersonSuffixPropertyサフィックス
kABPersonNicknamePropertyニックネーム
kABPersonFirstNamePhoneticPropertyファーストネームの読み
kABPersonLastNamePhoneticPropertyラストネームの読み
kABPersonMiddleNamePhoneticPropertyミドルネームの読み
kABPersonOrganizationProperty組織
kABPersonJobTitleProperty役職
kABPersonDepartmentProperty部門
kABPersonDepartmentPropertyEメール
kABPersonBirthdayProperty誕生日
kABPersonNotePropertyメモ
kABPersonCreationDateProperty作成日
kABPersonModificationDateProperty更新日
kABPersonAddressProperty住所
kABPersonDateProperty日付
kABPersonKindProperty種別
kABPersonPhoneProperty電話番号
kABPersonInstantMessagePropertyインスタントメッセージ
kABPersonURLPropertyURL
kABPersonRelatedNamesProperty関係

また、アドレスブックから取得した各種情報はCFReleaseで解放する必要があります。(ARCを使用していない場合)


2012年7月6日金曜日

Apple Developer登録の更新方法

アップルのデベロッパー登録は、1年ごとに更新する必要があります。

毎年やる度に試行錯誤することになってしまうので、備忘録として記録しておきます。

各種証明書の概念はこちらの説明に譲るとして、以下の流れで各種証明書を更新する必要があります。



1. アプリケーション→ユーティリティにあるキーチェーンアクセス.appを起動し、「iPhone Developer」証明書と「iPhone Distribution」証明書を削除する。(ログインタブとシステムタブにあります。念のため削除後に検索バーから「iPhone」で検索をかけて旧い証明書が検索されないか確認する。旧いのが残っていると、後でビルド時に証明書関連のエラーが出て時間を浪費することになるので注意。)

2. 「キーチェーンアクセス」→「証明書アシスタント」→「認証局に証明書を要求」後、「ユーザーのメールアドレス」、「通称」を入力、(「CAのメールアドレス」は空でも良い)要求の処理に「ディスクに保存」をチェック、「鍵ペア情報を生成」はチェック無しで「続ける」をクリックし、「CertificateSigningRequest.certSigningRequest」証明書をデスクトップに書き出し。

3. アップルのデベロッパーサイトを開き、Certificatesから「Request Certificate」をクリック。


4. 画面下部の「ファイルを選択」をクリックする。


5. 2で書き出した証明書ファイルを選択し、「submit」ボタンをクリックする。

6. 証明書の「Status」が「Pending」になるので、他のタブ(Distribution等)を選択後、適当な時間待ってから「Development」タブを選択する。

7. 「Status」が「Issued」に変わり、「Download」ボタンが押せるようになっているので、クリックして証明書(ios_development.cer)をダウンロードする。


8. ダウンロードした証明書(ios_development.cer)ファイルをダブルクリックする。(キーチェーンアクセス.appで新しい有効期限の証明書になっていることを確認する。)

9. 「Distribution」タブをクリックし、「Revoke」をクリック。確認画面で「OK」を押して期限切れ間近(または既に期限切れになっている)Distribution用証明書を削除する。


10. 「Request Certificate」ボタンが押せるようになっているのでクリックする。


11. 「ファイルを選択」をクリックする。


12. 2で書き出したファイルを選択し、「submit」ボタンをクリックする。

13. 証明書の「Status」が「Pending」になるので、他のタブ(Development等)を選択後、適当な時間待ってから「Distribution」タブを選択する。

14. 「Status」が「Issued」に変わり、「Download」ボタンが押せるようになっているので、クリックして証明書(ios_distribution.cer)をダウンロードする。


15. ダウンロードした証明書(ios_distribution.cer)ファイルをダブルクリックする。(キーチェーンアクセス.appで新しい有効期限の証明書になっていることを確認する。)

16. 以上で終了です。App IDとDevice IDは変更する必要はありませんが、プロビジョニングプロファイルは再作成&ダウンロードする必要があります。



2012年6月4日月曜日

iOSアプリの証明書まとめ

iOSアプリを実機で動作させるには、iOS Developer Centerでいくつか証明書を作成・登録する必要があります。
以前、アップルのマニュアルなどを見て設定は行っているのですが、1年ごとの更新の度に忘れてしまうのでまとめておきます。

まずは概要からです。

以下、上の概要の手順を、1つずつ少し詳しく書いたものです。
まず、証明書署名要求ファイルの登録です。


次に、デバイスの登録です。

次に、アプリケーションの登録です。


次に、プロビジョニングプロファイルの登録です。



 最後に、ビルドします。



2012年1月20日金曜日

isaって何?

デバッグをしていると、よくisaという変数を目にすることがあります。
isaは、NSObjectが持っている構造体で、スーパークラスへのポインタや、メソッドの情報を保持しています。

例えば、NSMutableStringはNSStringクラスを継承したクラスですが、NSMutableStringのisa変数の中にNSStringへのポインタを保持しています。

また、NSMutableStringは「stringByAppendingString:」というメソッドを持っていますが、このメソッドの情報を以下の様にNSMutableStringのisa変数に格納しています。

セレクタ メソッド名の文字列
 22   stringByAppendingString:
 33   メソッドA
 44   メソッドB
 ・     ・
 ・     ・
 ・     ・

上の例のセレクタは説明の為適当な値です。また、セレクタはchar*型ですが、実際にはint型の数値が入ります。
Objective-Cのコンパイラは、メソッドの呼び出しを以下の様にC言語のコードに展開します。

[objA stringByAppendingString:objB];
  ↓
objc_msgSend(objA, 22, objB);

「objc_msgSend」関数は、objAにあるセレクタ22に対してobjBを送信してくれるC言語の関数です。
objc_msgSend関数はobjAの中にあるisa変数を参照して、指定されたセレクタ(22)を検索します。そしてそこにあったメソッドにobjBを渡すとともに呼び出してくれます。
ここで該当するセレクタがisaの中に見つからなかった場合は、スーパークラスであるNSStringのポインタを取得し、NSStringのisaに対して同様の処理を行います。
NSStringでも見つからなければ更にその継承元・・という風に辿っていき、最終的にNSObjectでも見つからなければ例外を発生させるという流れになります。

このような処理を行うことで、Objective-Cではプログラム実行時に動的にメソッドを入れ替えることがし易くなっているのです。

ただし、C言語のように直にポインタを呼び出すとかではないので動作は遅くなるはずです。また、C++言語と比べてもObjective-Cは検索処理などが行わる為、動作はかなり遅くなるはずです。(C++言語はクラスメソッドのジャンプテーブルを参照するのでC言語より数ステップ程度動作は遅くなります。)
多分ですが、メソッド呼び出しだけを見る限りC言語よりも数十倍〜数百倍は遅くなっているはずです。(遅さを補っても余りある利便性があるということです。)

2011年11月23日水曜日

UISearchBarに画像を設定する

UISearchBarに画像を設定するには、UISearchBarBackgroundビューを入れ替えてやります。


// searchBarはUISearchBarのインスタンス
for (UIView *v in searchBar.subviews) {
if ([NSStringFromClass(v.class) isEqualToString:@"UISearchBarBackground"]) {
[v removeFromSuperview];
}
}
UIImage *img = [UIImage imageNamed:@"custom_searchbar"];
UIImageView *iv = [[UIImageView alloc] initWithImage:img];
[searchBar insertSubview:iv atIndex:0];
[iv release];


1.UISearchBarのサブビューからUISearchBarBackgroundを探し、サーチバーから削除します。
2.UISearchBarBackgroundが合った場所にUIImageViewを挿入すれば画像が表示されるようになります。
  (custom_searchbarは背景に使用する画像ファイル名です。)

2011年7月12日火曜日

UILocalNotification

UILocalNotificationは、アプリケーションが終了した状態でも指定時間になったらメッセージを表示する等の動作を行わせることができる機能です。


// ローカル通知を作成
UILocalNotification *ln = [[UILocalNotification alloc] init];

NSDate *dt;
dt = [NSDate dateWithTimeIntervalSinceNow:60]; // 60秒後に表示

// 通知時刻を設定
[ln setFireDate:dt];

// タイムゾーンを設定
[ln setTimeZone:[NSTimeZone localTimeZone]];

// メッセージを設定
[ln setAlertBody:@"起きて"];

// サウンド設定
[ln setSoundName:UILocalNotificationDefaultSoundName];

// ボタンタイトルの設定
[ln setAlertAction:@"Open"];

// ローカル通知を登録
[[UIApplication sharedApplication] scheduleLocalNotification:ln];

[ln release];

上記例の処理をアプリに行わせて終了させると、アプリが動作していなくても60秒後に「起きて」というメッセージが表示されるようになります。

サウンド設定にはサウンドファイル名を拡張子付きで指定しますが、再生時間が30秒未満、かつ以下の形式でないと無効となり、システムのデフォルトサウンドが再生されてしまうので注意が必要です。

●リニアPCM
● MA4(IMA/ADPCM)
● µLaw
● aLaw

上記ファイルはaiff, caf, wavのファイルです。

また、applicationIconBadgeNumberプロパティに任意の数字を指定することで、メールアプリなどでよく見られるようなバッヂが表示されるようになります。



2011年7月7日木曜日

特殊フォルダのパスを取得する

ドキュメントフォルダなどのパスの一覧を取得するには、NSSearchPathForDirectoriesInDomain関数を使用します。


NSArray * NSSearchPathForDirectoriesInDomains (
   NSSearchPathDirectory directory,
   NSSearchPathDomainMask domainMask,
   BOOL expandTilde
);


NSSearchPathDirectoryは、取得するパスの種類を指定します。
定数内容
NSApplicationDirectoryアプリケーションディレクトリ
(/Applications)
NSDemoApplicationDirectoryデモバージョンアプリケーション
ディレクトリ
(/Demos)
NSDeveloperApplicationDirectory開発用アプリケーションディレクトリ
(/Developer/Applications)
NSAdminApplicationDirectoryシステムとネットワーク管理者用
アプリケーションディレクトリ
(/Administration)
NSLibraryDirectoryライブラリディレクトリ
NSDeveloperDirectory開発ツールディレクトリ(/Developer)
NSUserDirectoryユーザディレクトリ(/Users)
NSDocumentationDirectoryドキュメンテーションディレクトリ
(/Library/Documentation)
NSDocumentDirectoryドキュメントディレクトリ
NSCoreServiceDirectoryコアサービスディレクトリ
(/System/Libary/CoreServices)
NSAutosavedInformationDirectory自動保存ディレクトリ
(~/Library/Autosave Information)
NSDesktopDirectoryデスクトップ
NSCachesDirectoryキャッシュディレクトリ
(Library/Caches)
NSApplicationSupportDirectoryアプリケーションサポート
ディレクトリ
(Library/Application Support)
NSDownloadsDirectoryダウンロードディレクトリ
NSInputMethodsDirectoryインプットメソッドディレクトリ
(Library/Input Methods)
NSMoviesDirectoryムービーディレクトリ(~/Movies)
NSMusicDirectoryミュージックディレクトリ(~/Musics)
NSPicturesDirectoryピクチャディレクトリ(~/Picture)
NSPrinterDescriptionDirectoryプリンタ情報ディレクトリ
(Library/Printers/PPDs)
NSSharedPublicDirectoryパブリックディレクトリ(~/Public)
NSPreferencePanesDirectoryプリファレンスペインディレクトリ
(~/PreferencePanes)
NSItemReplacementDirectory「NSFileManager」クラスの
「URLForDirectory:inDomain
:appropriateForURL:create:error:」
メソッドと組み合わせて使用する定数
NSAllApplicationsDirectory全アプリケーションディレクトリ
NSAllLibrariesDirectory全ライブラリディレクトリ


NSSearchPathDomainMaskは、取得するパスの範囲を指定します。
定数内容
NSUserDomainMaskユーザーのホームディレクトリ
NSLocalDomainMaskローカルマシン内
NSNetworkDomainMaskネットワーク内(/Network等)
NSSystemDomainMaskシステム内(/System)
NSAllDomainsMask全てのドメイン


expandTildeは、パスが長い場合にチルダをフルパスにするかを指定します。

NSString文字の配列がNSArrayに返却されます。

アプリで作成したドキュメントをMacと共有できるようにする

iPhoneアプリで作成したドキュメントをMacと共有するには、アプリのInfo.plistファイルを編集します。

ビルド設定の「Info」タグに行を追加し、「Application supports iTunes file sharing」 を追加し、タイプをBOOL型にしてYESにします。

これでiTunesのAppタグからアプリのドキュメントを共有できるようになります。

2011年7月5日火曜日

codesign failed

アップルのデベロッパー登録は1年毎に更新する必要がありますが、この時アップルの説明書の通りに更新しても、いざ新しくビルドを行おうとする時に以下のエラーが出てハマる場合があります。


iPhone Developer: ・・・・・・・・: ambiguous (matches "iPhone Developer: ・・・・・・・・・" in /Library/Keychains/System.keychain and "iPhone Developer: ・・・・・・・・・" in /Users/・・・・・/Library/Keychains/login.keychain)
Command /usr/bin/codesign failed with exit code 1

・・・の部分は開発者ごとに異なります。

この原因はキーチェインに以前登録した古い開発者用の証明書が残っていて、「証明書ユーティリティが新しいのと古いのとどっちを使っていいのか分からない」のが原因みたいです。
この場合、 【アプリケーション】→【ユーティリティ】にあるキーチェーンアクセスを起動してみると、☓印がついた証明書(期限切れの証明書)があると思うので、削除します。

☓印がついた証明書が見えない場合もあり(私はこれでハマりました)、この場合は検索ボックスで「iphone」と検索すると表示されるようになります。


【追記】
xcode4.0.1を使っている時にエラーが出る場合もあります。
この場合、xcode4.1がひっそりとリリースされていて、をれを使えば直る場合があります。
バージョン4以降は【product】→【edit scheme】でArchiveのBuild ActionをDistributionに変えておくのも忘れないようにしましょう。

【更に追記】
同じcodesignエラーですが、エラーの内容が以下の様になる場合があります。

Code sign error: The identity "iPhone Developer" doesn't match any valid certificate / private key pair in the default keychain.

これはキーチェインがデフォルトになっていないからだそうです。
【アプリケーション】→【ユーティリティ】でキーチェインアクセスを起動し、左上の窓からログインを右クリック。【キーチェーン”ログイン”をデフォルトにする】を選択するとエラーが出なくなります。

2011年6月4日土曜日

実機とシミュレータで挙動を変える

ごくまれにですが、実機では動くのにシミュレータでは動かない命令があります。

例えば、iAd広告を表示する時、ADBannerViewのrequiredContentSizeIdentifiersプロパティを設定しようとすると何故かシミュレータでは動きません。

このような場合等に実機とシミュレータで挙動を切り替える必要が出てきますが、シミュレータ上だけで有効になるデファイン値が用意されています。


#ifndef TARGET_IPHONE_SIMULATOR
・
・
#endif

#ifndefで囲んだ部分は、シミュレータ用のビルドではビルドされません。

2011年4月18日月曜日

Subviewを最前面・再背面に移動する

特定のSubviewを最前面に移動する場合、bringSubviewToFrontメソッドを使用します。
逆に、再背面に移動したい場合、sendSubviewToBackメソッドを使用します。


[self.view bringSubviewToFront:最前面に移動したいView];
[self.view sendSubviewToBack:最背面に移動したいView]

2011年3月25日金曜日

GameKitで小数を送信する

GameKitでスコアやランキングデータを送信する時、Appleのドキュメントでは整数(int64)のデータを送信する方法しか載っていないので、タイムなどの小数(45.67秒等)を送信したい場合は一瞬戸惑います。

小数を送信する場合、NSNumberクラスを使用します。
NSNumberクラスはBoolやdouble,Int型などのprimitive型をオブジェクトにしたい場合に使用する便利なクラスです。


-(void)reportScores:(double)score forCategory:(NSString*)category
{
scoreReporter = [[[GKScore alloc] initWithCategory:category] autorelease];
NSNumber *numScore = [NSNumber numberWithDouble:round(score * 100)];
scoreReporter.value = numScore.intValue;
[scoreReporter reportScoreWithCompletionHandler:^(NSError* error) {
if (error != nil) {
// エラー処理
return;
}
}];
}


Appleのドキュメントに載っているスコア送信例を、double型を送信するように変更してあります。
上の例では、少数以下2桁を送信する例です。
引数のdouble型scoreを整数にしたいので100倍してNSNumber型のオブジェクトにします。
その後、そのint値(64ビット値)をGKScoreオブジェクトに設定しています。
勿論、iTunesでリーダーボードのスコア設定をDecimal小数2桁等、小数を扱うように設定しておく必要はあります。

後はAppleドキュメントに載っている様にランキング受信処理でGKScoreのformattedValueプロパティを参照すれば、iTunesで設定したフォーマットでNSString文字列が取得できます。

2011年3月2日水曜日

IOS4以降のアニメーション

IOS4以降のアニメーションは、[UIView animateWithDulation:delay:options:animations:completion:]を使います。IOS4以降でも従来のアニメーションは行えるようですが、非推奨となってしまったようです。
もっと簡易的に使えるメソッドもありますが、これだけ覚えておけば事足りるかもしれません。


[UIView animateWithDuration:0.5
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
button.alpha = 0.0; // UIButtonインスタンスを徐々に透明に
}
completion:^(BOOL finished){
button.alpha = 1.0f; // buttonを現す
}];


第一引数はアニメーションにかける時間を指定します。上記例では0.5秒間でボタンを徐々に透明にしていきます。
delayはアニメーションを開始する時間を指定します。上記例では0なので即時アニメーション開始です。
optionsはアニメーションの動作のオプションです。上記例ではEaseOutで徐々に速度を落としてアニメーションをさせています。
animationsが実際のアニメーションの指定です。
completionはアニメーションが終了した時に呼び出される処理です。上記例では徐々に透明になったボタンの姿を再度現すようにしています。

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を解放