Cocoa練習帳 -36ページ目

[iOS]初期設定NSUserDefaults(その2)

アプリケーションに初期設定情報を持たせる場合、バージョン管理が重要になる。できれば、最初の版からバージョン情報を持たせる事を薦める。



バージョン情報は、新旧の比較を考えると数値型の方が便利だと思うが、バージョンが異なると以前の初期設定を初期化していいのなら、アプリケーションのバージョン番号と同じ内容を文字列として持たせても問題ないと考えている。



@interface Document : NSObject

@property (strong, nonatomic) NSString *version;
@property (strong, nonatomic) NSString *message;

- (void)clearDefaults;
- (void)updateDefaults;
- (void)loadDefaults;
@end


Documentクラスに、バージョン情報が一致しない場合は初期化するコードを追加する。



@implementation Document
@synthesize version = _version;
@synthesize message = _message;

- (id)init
{
if ((self = [super init]) != nil) {
NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
self.version = [infoDictionary objectForKey:@"CFBundleShortVersionString"];

NSString *aVersion = @"1.0";
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"version"]) {
aVersion = [[NSUserDefaults standardUserDefaults] objectForKey:@"version"];
}
if ([aVersion compare:self.version] != NSOrderedSame) {
[self clearDefaults];
}
}
return self;
}

- (void)dealloc
{
self.version = nil;
self.message = nil;
}

- (void)clearDefaults
{
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"message"]) {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"message"];
}
}

- (void)updateDefaults
{
BOOL fUpdate = NO;

NSString *aVersion = @"";
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"version"]) {
aVersion = [[NSUserDefaults standardUserDefaults] objectForKey:@"version"];
}
if (self.version) {
if ([aVersion compare:self.version] != NSOrderedSame) {
[[NSUserDefaults standardUserDefaults] setObject:self.version forKey:@"version"];
fUpdate = YES;
}
}

NSString *aMessage = @"";
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"message"]) {
aMessage = [[NSUserDefaults standardUserDefaults] objectForKey:@"message"];
}
if (self.message) {
if ([aMessage compare:self.message] != NSOrderedSame) {
[[NSUserDefaults standardUserDefaults] setObject:self.message forKey:@"message"];
fUpdate = YES;
}
}

if (fUpdate) {
[[NSUserDefaults standardUserDefaults] synchronize];
}
}

- (void)loadDefaults
{
NSString *aVersion = @"";
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"version"]) {
aVersion = [[NSUserDefaults standardUserDefaults] objectForKey:@"version"];
}
if ([aVersion compare:self.version] != NSOrderedSame) {
[self clearDefaults];
}
else {
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"message"]) {
self.message = [[NSUserDefaults standardUserDefaults] objectForKey:@"message"];
}
}
}
@end


ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/Defaults - GitHub

関連情報
Data Management Coding How-To's

iOS Developer Libraryの情報です。

[iOS]初期設定NSUserDefaults

アプリケーションの初期設定の情報は、NSUserDefaultsクラスを使えば実現できる。ただ、単純にNSUserDefaultsを使って値を読み書きするだけだと、扱う情報が増えてくると複雑になってくるので、先日紹介したDocumentクラスを使った実装を紹介する。



自分のアプリケーション用のDocumentクラスを用意する。



@interface Document : NSObject

@property (strong, nonatomic) NSString *message;

- (void)clearDefaults;
- (void)updateDefaults;
- (void)loadDefaults;
@end


初期設定の項目をプロパティとして持ち、読み書き処理をメソッドして用意する。以下が実装例だ。



@implementation Document
@synthesize message = _message;
- (void)clearDefaults
{
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"message"]) {
DBGMSG(@"remove message:%@", self.message);
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"message"];
}
}

- (void)updateDefaults
{
NSString *aMessage = nil;
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"message"]) {
aMessage = [[NSUserDefaults standardUserDefaults] objectForKey:@"message"];
}
if (self.message) {
if ((aMessage) && ([aMessage compare:self.message] == NSOrderedSame)) {
}
else {
[[NSUserDefaults standardUserDefaults] setObject:self.message forKey:@"message"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
}

- (void)loadDefaults
{
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"message"]) {
self.message = [[NSUserDefaults standardUserDefaults] objectForKey:@"message"];
}
}
@end


iOSでは、ユーザに保存という処理を意識させないのが優れたUIの条件のようだ。アプリケーションのデリゲート・クラスでアプリケーションが非アクティブになるタイミングで初期設定の値を保存する。



@implementation AppDelegate
@synthesize window = _window;
@synthesize viewController = _viewController;
@synthesize document = _document;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.document = [[Document alloc] init];
[self.document loadDefaults];

self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self.document updateDefaults];
}

- (void)applicationWillTerminate:(UIApplication *)application
{
[self.document updateDefaults];
}
@end


ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/Defaults - GitHub

関連情報
Data Management Coding How-To's

iOS Developer Libraryの情報です。

[iOS]グラフ描画ライブラリ

iPhone用グラフ描画ライブラリで、軽量なものとして、S7GraphViewというものがある。自分のアプリケーションでも利用しているのがこれだ。



個人的には気に入っているのだが、最近は更新が滞り、先日、改良コードを送付したのだが、返信がない。そして、久しぶりにサイトをアクセスしたところ、無くなったようだ。



http://code.google.com/p/s7graphview/

その為、困ってしまったので、何か継承できたらと考えている。



ただし、パクリにならないよう、継承するのなら、一から設計し直すつもりだ。



関連情報
http://code.google.com/p/s7graphview/

S7GraphViewのサイト。残念ながら、閉鎖されたようだ。

MOSA新年会

本日は、MOSA新年会という事で、日記はこれまで。許してください。

$BITZ Weblog

ちなみに、明日も。。。

[Mac][iOS]無名カテゴリ

Objective-Cで、非公開メソッドが必要になった、無名カテゴリで実現できる。



@interface MyClass : NSObject {
}
- (void)publicMethod;
@end

@interface MyClass ()
- (void)privateMethod;
@end


カテゴリには、メソッドしか宣言できない為、無名カテゴリで非公開に出来るのはメソッドのみと考えていたが、Clang/LLVM 2.0コンパイラを使うとプロパティでもOKのようだ。



@interface MyClass : NSObject
- (void)publicMethod;
@end

@interface MyClass ()
@property (nonatomic, strong) NSMutableString *string;
- (void)privateMethod;
@end

@implementation MyClass
@synthesize string = _string;
@end


通常のクラス宣言部にも同じプロパティの宣言をして、公開用と非公開用で宣言の内容を変える事も可能なようだ。



@interface MyClass : NSObject
@property (nonatomic, strong, readonly) NSMutableString *string;
- (void)publicMethod;
@end

@interface MyClass ()
@property (nonatomic, strong, readwrite) NSMutableString *string;
- (void)privateMethod;
@end

@implementation MyClass
@synthesize string = _string;
@end


無名カテゴリは、AppleのDeveloper Libraryの説明によると、匿名のカテゴリに似ているが、正確にはクラス拡張と呼ぶ、ちょっと、異なる物のようだ。



ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/mac/anonymous_categories - GitHub

関連情報
Categories and Extensions

iOS Developer LibraryのThe Objective-C Programming Languageでの無名カテゴリの説明です。

[iOS]MVCのモデルについて

MVCのモデルは、実際の値を持っているデータ構造と、それを抽象的に見せるインタフェースから成り立っていると考えている。ビジネス・ロジックは、このインタフェース部分で実装されると考えていて、このインタフェース部分を私はDocumentというクラスで実装している。



HMDT木下誠さんの著書『iOS開発におけるパターンによるオートマティズム』で、モデルレイヤ設計パターンとして、モデルオブジェクトクラスとモデルマネージャクラスが解説されているが、ほぼ、同様なものと考えられる。



BITZ Weblog-MVCのモデル

モデルの実装方法が決まったら、それをどうアクセスするかが問題となる。オッティモの小池邦人さんが、MOSADeNで説明されていたサンプルでは、アプリケーションのデリゲートクラスのインスタンス変数として持つ方法。



@interface AppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
MyViewController *viewController;
MyDocument *document;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet MyViewController *viewController;
@property (nonatomic, retain) MyDocument *document;
@end


こうすれば、アプリケーションのインスタンスを取得すれば、documentインスタンス変数にアクセスできる。



AppDelegate	*appl = nil;
appl = (AppDelegate *)[[UIApplication sharedApplication] delegate];


先ほどの、木下さんの著書では、シングルトンのパターンで実装されていた。



static MyDataManager *_sharedInstance = nil;
+ (MyDataManager *)sharedManager
{
if (!_sharedInstance) {
_sharedInstance = [[MyDataManager alloc] init];
}
return _sharedInstance;
}


好みの問題だと思われるが、私はデリゲートのインスタンス変数とする実装が好みだ。



関連情報
Carbon視点でiPhone探求(データソースとデリゲートを実装する)

オッティモの小池邦人さんがDocumentの実装について説明しているページです。
iOS開発におけるパターンによるオートマティズム

HMDTの木下誠さんが著書を紹介しているページです。

[iOS]iPhoneの向きを変更する

Xcodeで、ターゲットのSummaryのiPhone/iPod Deployment InfoのSupported Device Orienttationsで、対応する向きを選択する。(下記の図の赤い丸で囲まれたボタン)古い資料では、Info.plistのUIInterfaceOrientationキーのカスタマイズが説明されているが、この操作によってInfo.plistが更新される。



BITZ Weblog-info.plist

四つの項目(Portrait, Upside Down, Landscape Left, Landscape Right)の選択順には意味があって、最初に選択された物が、起動時の画面の向きという事になるようだ。



ユーザーがiPhoneの向きを変更するのに追従して、自動で画面の向きを変更したい場合は、ビュー・コントローラのshouldAutorotateToInterfaceOrientation:メソッドをカスタマイズする。



- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
BOOL result = YES;
if (interfaceOrientation == UIInterfaceOrientationPortrait)
result = YES;
else if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft)
result = YES;
else if (interfaceOrientation == UIInterfaceOrientationLandscapeRight)
result = YES;
else if (interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
result = NO;
return result;
}


例えば、上記の例は、上下が逆さま(UIInterfaceOrientationPortraitUpsideDown)以外が自動で向きが変わるというコードだ。



BITZ Weblog-UIInterfaceOrientationPortrait
BITZ Weblog-UIInterfaceOrientationLandscapeLeft

ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/Autorotation - GitHub

関連情報
User Experience Coding How-To's

iOS Developer Libraryの情報です。

[iOS]Twitter機能を組み込む

iOS5から、Twitter Frameworkが追加され、自分のアプリケーションにTwitter機能を追加するのが楽になったが、それ以前は、独自にTwitter機能を実装するか、オープンソースのフレームワークを利用していた。


この情報は、iOS5以降は不要なのかもしれないが、理由があって、古いOSに対応しなければならばい際に役立つと考えている。



少し、ややこしい話だが、iOSで著名なTwitterフレームワークにMGTwitterEngineというのがあったのだが、Twitterの認証方法がBASIC認証からOAuth/xAuthに移行した際、このフレームワークをベースに作成されたxAuthに対応したXAuthTwitterEngineというのが使われだした。ただ、その後、MGTwitterEngineがxAuthに対応したので、利用上問題がなければこちらを利用すればいいと考えている。



MGTwitterEngineは、以下のサイトから入手できる。

Matt Gemmell



ソースコードは、GitHubで公開されている。

https://github.com/mattgemmell/MGTwitterEngine



ただし、MGTwitterEngineは、上記のソースコードだけで完結していなくて、使い方によって、他のオープンソースのライブラリが必要になる。例えば、TouchJSONやOAuthConsumer、yajl等。



自分のアプリケーションでTwitter機能を利用する為には、使用する機能によっては、Twitter社への申請が必要になる。




  • 新規アプリ申請

  • Consumer Key取得

  • xAuth認証の使用許可申請



申請のサイトは、以下のURLだ。

http://dev.twitter.com/apps/new



ただし、最後の『xAuth認証の使用許可申請』については、新規アプリ申請後に、電子メールで依頼する事になる。



以下が、私が出したメールのサンプルだ。参考にして欲しい。




To: api@twitter.com
Subject: Please apply this app to use xAuth

Hello.

My name is 名前.

I am a developer of "アプリケーション名"
"アプリケーション名" is a iPhone application.

<アプリケーションの説明> ※英語なので辛い!

Please apply this app to use xAuth.

Application Name: 申請したアプリケーション名
My account: @アカウント名 ※Twitterの
Consumer Key: xxxxxxxx

Thank you.


xAuthの申請について、Googleで検索すると、追加の手続きが必要という情報もあるようだが、私の場合は、既にリリース済みのアプリケーションだった為か、すんなりといきました。



関連情報
Matt Gemmell

MGTwitterEngineの開発者のサイト。

[Mac]高分解能実時間

時間を計測する関数には、色々な種類がある。どのような精度の値が取得できるかも重要だが、その関数を呼び出すコストも重要になる。



以前、Cocoa勉強会の発表の為に調べた、各種の時間取得関数は以下のとおり。現在では、使用できない物もあるが、そのまま、掲載している。




















































































種類 モジュール 関数
ANSI C 標準ライブラリ clock
time
Mach Clock Interface clock_get_time
Mach Absolute Time Units mach_absolute_time
カーネル開発 カーネル・ライブラリ clock_get_uptime
clock_get_system_value
clock_get_calendar_value
clock_get_calendar_offset
BSD POSIX gettimeofday
Core Foundation CarbonLib CFAbsoluteTimeGetCurrent
Carbon デバイス・ドライバー UpTime
OS Utilities ReadDateTime
Date, Time, and Measurement Utilities GetDateTime
Cocoa NSDate +timeIntervalSince1970:
+timeIntervalSinceReferenceDate:


Lionで呼び出す事が可能な関数について、複数回のループ内で呼び出して、ループ全体にかかる時間を測定したのが、以下のコード。



#import <Foundation/Foundation.h>
#import <stdio.h>
#import <sys/time.h>
#import <mach/mach.h>
/* #import <CoreServices/CoreServices.h> */

#define TIMERSUB(a, b, result) \
do { \
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
if ((result)->tv_usec < 0) { \
--(result)->tv_sec; \
(result)->tv_usec += 1000000; \
} \
} while (0)

int main (int argc, const char * argv[])
{
int i, j;
clock_serv_t clock_serv;
mach_timespec_t cur_time;
struct timeval tv_start, tv_end, tv_diff;
/* AbsoluteTime at_start; */
/* float deltaTime; */
/* uint64_t result; */
/* unsigned long secs; */

@autoreleasepool {

/* ANSI C: clock_t clock(void) */
gettimeofday(&tv_start, NULL);
for (i = 0; i < 10000; i++) {
for (j = 0; j < 100; j++) {
clock();
}
}
gettimeofday(&tv_end, NULL);
TIMERSUB(&tv_end, &tv_start, &tv_diff);
printf("ANSI C: clock: %ld.%06d[sec]\n", tv_diff.tv_sec, tv_diff.tv_usec);

/* ANSI C: time_t time(time_t *pSec) */
gettimeofday(&tv_start, NULL);
for (i = 0; i < 10000; i++) {
for (j = 0; j < 100; j++) {
time(NULL);
}
}
gettimeofday(&tv_end, NULL);
TIMERSUB(&tv_end, &tv_start, &tv_diff);
printf("ANSI C: time: %ld.%06d[sec]\n", tv_diff.tv_sec, tv_diff.tv_usec);

/* Mach: kern_return_t clock_get_time(clock_serv_t clock_serv, mach_timespec_t *cur_time) */
gettimeofday(&tv_start, NULL);
for (i = 0; i < 10000; i++) {
for (j = 0; j < 100; j++) {
host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &clock_serv);
clock_get_time(clock_serv, &cur_time);
}
}
gettimeofday(&tv_end, NULL);
TIMERSUB(&tv_end, &tv_start, &tv_diff);
printf("Mach: clock_get_time: %ld.%06d[sec]\n", tv_diff.tv_sec, tv_diff.tv_usec);

/* Mach: uint64_t mach_absolute_time(void) */
gettimeofday(&tv_start, NULL);
for (i = 0; i < 10000; i++) {
for (j = 0; j < 100; j++) {
mach_absolute_time();
}
}
gettimeofday(&tv_end, NULL);
TIMERSUB(&tv_end, &tv_start, &tv_diff);
printf("Mach: mach_absolute_time: %ld.%06d[sec]\n", tv_diff.tv_sec, tv_diff.tv_usec);

#if 0
/* Kernel: void clock_get_uptime(uint64_t *result) */
gettimeofday(&tv_start, NULL);
for (i = 0; i < 10000; i++) {
for (j = 0; j < 100; j++) {
clock_get_uptime(&result);
}
}
gettimeofday(&tv_end, NULL);
TIMERSUB(&tv_end, &tv_start, &tv_diff);
printf("Mach: clock_get_uptime: %ld.%06d[sec]\n", tv_diff.tv_sec, tv_diff.tv_usec);

/* Kernel: mach_timespec_t clock_get_system_value(void) */
gettimeofday(&tv_start, NULL);
for (i = 0; i < 10000; i++) {
for (j = 0; j < 100; j++) {
clock_get_system_value();
}
}
gettimeofday(&tv_end, NULL);
TIMERSUB(&tv_end, &tv_start, &tv_diff);
printf("Mach: clock_get_system_value: %ld.%06d[sec]\n", tv_diff.tv_sec, tv_diff.tv_usec);

/* Kernel: mach_timespec_t clock_get_calendar_value(void) */
gettimeofday(&tv_start, NULL);
for (i = 0; i < 10000; i++) {
for (j = 0; j < 100; j++) {
clock_get_calendar_value();
}
}
gettimeofday(&tv_end, NULL);
TIMERSUB(&tv_end, &tv_start, &tv_diff);
printf("Mach: clock_get_calendar_value: %ld.%06d[sec]\n", tv_diff.tv_sec, tv_diff.tv_usec);

/* Kernel: mach_timespec_t clock_get_calendar_offset(void) */
gettimeofday(&tv_start, NULL);
for (i = 0; i < 10000; i++) {
for (j = 0; j < 100; j++) {
clock_get_calendar_offset();
}
}
gettimeofday(&tv_end, NULL);
TIMERSUB(&tv_end, &tv_start, &tv_diff);
printf("Mach: clock_get_calendar_offset: %ld.%06d[sec]\n", tv_diff.tv_sec, tv_diff.tv_usec);
#endif /* 0 */

/* POSIX: int gettimeofday(struct timeval *tv, struct timezone *tz) */
gettimeofday(&tv_start, NULL);
for (i = 0; i < 10000; i++) {
for (j = 0; j < 100; j++) {
gettimeofday(&tv_end, NULL);
}
}
gettimeofday(&tv_end, NULL);
TIMERSUB(&tv_end, &tv_start, &tv_diff);
printf("POSIX: gettimeofday: %ld.%06d[sec]\n", tv_diff.tv_sec, tv_diff.tv_usec);

/* Core Foundation: CFAbsoluteTimeGetCurrent */
gettimeofday(&tv_start, NULL);
for (i = 0; i < 10000; i++) {
for (j = 0; j < 100; j++) {
CFAbsoluteTimeGetCurrent();
}
}
gettimeofday(&tv_end, NULL);
TIMERSUB(&tv_end, &tv_start, &tv_diff);
printf("Core Foundation: CFAbsoluteTimeGetCurrent: %ld.%06d[sec]\n", tv_diff.tv_sec, tv_diff.tv_usec);

#if 0
/* Carbon: UpTime */
gettimeofday(&tv_start, NULL);
for (i = 0; i < 10000; i++) {
for (j = 0; j < 100; j++) {
UpTime();
}
}
gettimeofday(&tv_end, NULL);
TIMERSUB(&tv_end, &tv_start, &tv_diff);
printf("Carbon: UpTime: %ld.%06d[sec]\n", tv_diff.tv_sec, tv_diff.tv_usec);
#endif /* 0 */

#if 0
/* Carbon: ReadDateTime */
gettimeofday(&tv_start, NULL);
for (i = 0; i < 10000; i++) {
for (j = 0; j < 100; j++) {
ReadDateTime(&secs);
}
}
gettimeofday(&tv_end, NULL);
TIMERSUB(&tv_end, &tv_start, &tv_diff);
printf("Carbon: ReadDateTime: %ld.%06d[sec]\n", tv_diff.tv_sec, tv_diff.tv_usec);
#endif /* 0 */

#if 0
/* Carbon: GetDateTime */
gettimeofday(&tv_start, NULL);
for (i = 0; i < 10000; i++) {
for (j = 0; j < 100; j++) {
GetDateTime(&secs);
}
}
gettimeofday(&tv_end, NULL);
TIMERSUB(&tv_end, &tv_start, &tv_diff);
printf("Carbon: GetDateTime: %ld.%06d[sec]\n", tv_diff.tv_sec, tv_diff.tv_usec);
#endif /* 0 */

/* Cocoa: NSDate::timeIntervalSince1970 */
gettimeofday(&tv_start, NULL);
for (i = 0; i < 10000; i++) {
for (j = 0; j < 100; j++) {
[[NSDate date] timeIntervalSince1970];
}
}
gettimeofday(&tv_end, NULL);
TIMERSUB(&tv_end, &tv_start, &tv_diff);
printf("Cocoa: NSDate::timeIntervalSince1970: %ld.%06d[sec]\n", tv_diff.tv_sec, tv_diff.tv_usec);

/* Cocoa: NSDate::timeIntervalSinceReferenceDate */
gettimeofday(&tv_start, NULL);
for (i = 0; i < 10000; i++) {
for (j = 0; j < 100; j++) {
[[NSDate date] timeIntervalSinceReferenceDate];
}
}
gettimeofday(&tv_end, NULL);
TIMERSUB(&tv_end, &tv_start, &tv_diff);
printf("Cocoa: NSDate::timeIntervalSinceReferenceDate: %ld.%06d[sec]\n", tv_diff.tv_sec, tv_diff.tv_usec);

#if 0
/* Unsupported: hrtime_t gethrtime(void) */
gettimeofday(&tv_start, NULL);
for (i = 0; i < 10000; i++) {
for (j = 0; j < 100; j++) {
gethrtime();
}
}
gettimeofday(&tv_end, NULL);
TIMERSUB(&tv_end, &tv_start, &tv_diff);
printf("Unsupported: gethrtime: %ld.%06ld[sec]\n", tv_diff.tv_sec, tv_diff.tv_usec);
#endif /* 0 */

}
return 0;
}


mach_absolute_timeの呼び出しコストが最も軽いみたい。



ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/mac/hosttime - GitHub

関連情報
Technical Q&A QA1398 Mach Absolute Time Unit

mach_absolute_timeの説明です。

Technical Q&A QA1643 Audio Host Time On iPhone OS

iOSで、AudioGetCurrentHostTimeとAudioGetHostClockFrequencyに代わってmach_absolute_timeを利用する説明です。

[iOS]モーダルViewController(その2)

昨日、説明したモーダル・ビュー。iOS4から利用できる様になったBlocksを使えば、もっと、簡単に記述できる、はず。多分。



モーダル・ビューで、Blocksと扱うイベントを定義する。



typedef enum ModalPaneViewControllerResult {
ModalPaneViewControllerResultCancelled,
ModalPaneViewControllerResultDone
} ModalPaneViewControllerResult;

typedef void (^ModalPaneViewControllerCompletionHandler)(ModalPaneViewControllerResult result);


モーダル・ビューで、delegateプロパティの代わって、Blocksをプロパティとして追加する。



@interface ModalPaneViewController : UIViewController

@property (nonatomic, copy) ModalPaneViewControllerCompletionHandler completionHandler;

- (IBAction)done:(id)sender;
- (IBAction)cancel:(id)sender;
@end


モーダル・ビューで、デリゲートのメソッドを呼んであげる代わりに、Blocksを呼ぶ。



@implementation ModalPaneViewController

@synthesize completionHandler = _completionHandler;

- (IBAction)done:(id)sender
{
if (self.completionHandler) {
self.completionHandler(ModalPaneViewControllerResultDone);
}
}

- (IBAction)cancel:(id)sender
{
if (self.completionHandler) {
self.completionHandler(ModalPaneViewControllerResultCancelled);
}
}

@end


呼び出し元のモーダル・ビューを開くコードは以下のとなる。



- (IBAction)modalPane:(id)sender
{
ModalPaneViewController *viewController = [[ModalPaneViewController alloc]
initWithNibName:@"ModalPaneViewController"
bundle:nil];
[viewController setCompletionHandler:^(ModalPaneViewControllerResult result) {
switch (result) {
case ModalPaneViewControllerResultCancelled:
[self performSelectorOnMainThread:@selector(didCencel:) withObject:nil waitUntilDone:NO];
break;
case ModalPaneViewControllerResultDone:
[self performSelectorOnMainThread:@selector(didDone:) withObject:nil waitUntilDone:NO];
break;
default:
break;
}

[self dismissModalViewControllerAnimated:YES];
}];
[self presentModalViewController:viewController animated:YES];
}

- (void)didDone:(id)arg { }

- (void)didCencel:(id)arg { }


確かにスッキリした気がするが、楽になったかどうかは。。。



ソースコード
GitHubからどうぞ。

https://github.com/murakami/ModalPane - GitHub

関連情報
http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ModalViewControllers/ModalViewControllers.html

ADCの情報です。