トップ 最新 追記

Cocoa練習帳

iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど

2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|

2012-01-11 災害関連のTwitterハッシュタグ

_ 節電対策アプリケーションの制作時に参考にした情報です。

■日別の停電や公共交通機関のハッシュタグ(3月15日の例)
#315teiden: 停電情報
#315train: 電車の運行状況
#315bus: バスの運行状況
#315car: 道路交通状況
#315plane: 飛行機の運行状況
 
■節電対策
#brownout 節電対策の情報
#setsuden 節電対策の情報
#jishin 地震
#coolbiz クールビズ
 
■地域別情報のハッシュタグ
#save_miyagi 宮城県の情報
#save_fukushima 福島県の情報
#save_ibaraki 茨城県の情報
#save_iwate 岩手県の情報
#save_aomori 青森県の情報
#save_yamagata 山形県の情報
#save_niigata 新潟の情報
#save_nagano 長野の情報
 
■地震関連ハッシュタグ
#jishin: 地震一般に関する情報
#j_j_helpme :救助要請
#hinan :避難
#anpi :安否確認
#311care: 医療系被災者支援情報
#311sppt: 本当に支援が必要な現地の方々の生の声のみ
 
【東日本大震災】ネットで確認できる災害情報まとめ
http://freesoft-100.com/topic/disaster.html
 
東日本大震災 Twitter情報整理サイト
http://b.volunteer-platform.org/earthquake/
■twitter#setsuden#brownout#teiden
#315teiden 月日teiden
※7月4日は? #704teiden でいいのか?
※日本の年月日形式でいいのか?
 
http://search.twitter.com/search.json?q=%23setsuden
http://search.twitter.com/search?q=%23315teiden&format=iphone
 
■Go節電ガジェット
http://goo.gl/mESGj
http://www.gosetsuden.jp/static/gadget/index.html?view=mobile

2012-01-12 Cocoa Life KOF2011特別編


2012-01-13 GitHub

_ 今日から充実させていきたい、自分のGitHubのページ。


2012-01-14 画像

_ アプリケーションで利用しています画像は、以下のサイトから購入しました。


2012-01-15 アイコン

_ アプリケーションで利用していますアイコンは、以下のサイトで購入しました。


2012-01-16 tDiaryのsrc.rb

_ 使い方

src('ファイル名')
src_inline('文字列')
<%=src_inline <<TEXT
一行目
二行目
TEXT
%>

2012-01-17 雑記帳Tips

_ ■ディスク使用量の上位10件

% du -sk * | sort -nr | head -10

_ ■指定ディレクトリ配下のファイルを圧縮

% find ディレクトリ名 -print | xargs gzip

_ ■ディレクトリ単位のディスク使用量

% du -k -s ディレクトリ名

2012-01-18 デバッグ出力

_ 私は、XcodeのPrefix header(Supporting Files/プロジェクト名-Prefix.pch)に、必ず、以下のデバッグ出力のマクロを記述しています。

#ifdef	DEBUG
#define	DBGMSG(...)	NSLog(__VA_ARGS__)
#else	/* DEBUG */
#define	DBGMSG(...)
#endif	/* DEBUG */

_ また、ファイルの末尾には、ファイルが壊れたら分かるように、以下の注釈を必ず入れています。

/* End Of File */

2012-01-19 [iOS]モーダルViewController

例えば、Twitterアプリケーションの投稿画面の様に、iPhoneではモーダル・ビューがよく利用される。写真撮影で使われるUIImagePickerControllerもモーデル・ビューとして実装されていて、独自のモーダル・ビューを実装する場合の参考になる。

モーダル・ビューの為のUIViewControllerのサブクラスを用意する。 その際、モーダル・ビューの呼び出し元の為のデリゲートを作成する。

@class ModalPaneViewController;
 
@protocol ModalPaneViewControllerDelegate 
- (void)modalPaneViewControllerDidDone:(ModalPaneViewController *)modalPaneViewController;
- (void)modalPaneViewControllerDidCancel:(ModalPaneViewController *)modalPaneViewController;
@end

そして、モーダル・ビューのビュー・コントローラに呼び出し元のデリゲートをプロパティとして追加する。

@interface ModalPaneViewController : UIViewController
 
@property (nonatomic, weak) id   delegate;
 
- (IBAction)done:(id)sender;
- (IBAction)cancel:(id)sender;
@end

モーダル・ビューで、デリゲートのメソッドを呼んであげると、呼び出し元に制御が戻る。

@implementation ModalPaneViewController
 
@synthesize delegate = _delegate;
 
- (IBAction)done:(id)sender
{
    if ([self.delegate respondsToSelector:@selector(modalPaneViewControllerDidDone:)]) {
        [self.delegate modalPaneViewControllerDidDone:self];
    }
}
 
- (IBAction)cancel:(id)sender
{
    if ([self.delegate respondsToSelector:@selector(modalPaneViewControllerDidCancel:)]) {
        [self.delegate modalPaneViewControllerDidCancel:self];
    }
}
 
@end

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

- (IBAction)modalPane:(id)sender
{
    ModalPaneViewController *viewController = [[ModalPaneViewController alloc] 
                                               initWithNibName:@"ModalPaneViewController"
                                               bundle:nil];
    viewController.delegate = self;
    [self presentModalViewController:viewController animated:YES];
}

呼び出し元でデリゲートのメソッドを実装。その際に、モーダル・ビューを閉じるコードを呼ぶ。

- (void)modalPaneViewControllerDidDone:(ModalPaneViewController *)modalPaneViewController
{
    [self dismissModalViewControllerAnimated:YES];
}
 
- (void)modalPaneViewControllerDidCancel:(ModalPaneViewController *)modalPaneViewController
{
    [self dismissModalViewControllerAnimated:YES];
}

呼び出し元のボタンを押下すると、モーダル・ビューが表示され。

呼び出し元

モーダル・ビューのボタンを押下すると、モーダル・ビューは閉じる。

モーダル・ビュー

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/ModalPane - GitHub
本日のツッコミ(全2件) [ツッコミを入れる]

_ eien [delegateメソッドを呼ぶ前に、delegateがそれをサポートしているかどうか確認したほうが安全なコードになり..]

_ m_yukio [確かに、そうですね! 参考にさせていただきます。早速、修正しました。]


2012-01-20 [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

2012-01-21 [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の呼び出しコストが最も軽いみたい。

_ 関連情報

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を利用する説明です。

2012-01-22 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の開発者のサイト。

2012-01-23 [iOS]iPhoneの向きを変更する

Xcodeで、ターゲットのSummaryのiPhone/iPod Deployment InfoのSupported Device Orienttationsで、対応する向きを選択する。(下記の図の赤い丸で囲まれたボタン)古い資料では、Info.plistのUIInterfaceOrientationキーのカスタマイズが説明されているが、この操作によって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)以外が自動で向きが変わるというコードだ。

画像の説明 画像の説明

_ 関連情報

User Experience Coding How-To's
iOS Developer Libraryの情報です。
本日のツッコミ(全1件) [ツッコミを入れる]

_ eien [Appleは、 UIInterfaceOrientationIsLandscape UIInterfaceOri..]


2012-01-24 [iOS]MVCのモデルについて

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

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

画像の説明

モデルの実装方法が決まったら、それをどうアクセスするかが問題となる。オッティモの小池邦人さんが、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の木下誠さんが著書を紹介しているページです。

2012-01-25 [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の説明によると、匿名のカテゴリに似ているが、正確にはクラス拡張と呼ぶ、ちょっと、異なる物のようだ。

_ 関連情報

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

2012-01-27 MOSA新年会

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

画像の説明

_ ちなみに、明日も。。。


2012-01-29 [iOS]グラフ描画ライブラリ

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

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

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

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

_ 関連情報

http://code.google.com/p/s7graphview/
S7GraphViewのサイト。残念ながら、閉鎖されたようだ。

2012-01-30 [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

_ 関連情報

Data Management Coding How-To's
iOS Developer Libraryの情報です。

2012-01-31 [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

_ 関連情報

Data Management Coding How-To's
iOS Developer Libraryの情報です。

トップ 最新 追記