[iOS] ネットワーク接続状況取得ライブラリを公開

2011年5月25日水曜日 | Published in | 2 コメント

このエントリーをはてなブックマークに追加

ネットワークの接続状況を取得できるライブラリを公開しました。
dev5tec/FBNetworkReachability - GitHub


特徴


現在の接続状況(WiFi, 3Gなど)を取得することができる。

以下、使い方の例
FBNetworkReachability* network =
    [FBNetworkReachability networkReachabilityWithHostname:@"http://xcatsan.com/"];

FBNetworkReachabilityConnectionMode mode = network.connectionMode;
switch (mode) {
    case FBNetworkReachableNon:
        // 未接続
        break;

    case FBNetworkReachableWiFi:
        // WiFi接続
        break;

    case FBNetworkReachableWWAN:
        // 3G接続
        break;
}
if(network.reachable) {
    // 接続時処理
}

NSLog(@"ip: %@", network.ipaddress);  // IPアドレス

接続状況が変化した時には FBNetworkReachabilityDidChangeNotification をポストするので、この変化をトリガーにして非同期的な処理を書くこともできる。
[[NSNotificationCenter defaultCenter]
             addObserver:self
             selector:@selector(didChangeNetworkReachability:)
             name:FBNetworkReachabilityDidChangeNotification
             object:nil];

- (void)didChangeNetworkReachability:(NSNotification*)notification
{
    FBNetworkReachabiity* network = [notification object];
        :
}

その他の特徴は以下のとおり。

  • バックグラウンド動作は行わない。
  • バックグラウンドからフォアグラウンドに変わる間に変化があった場合は最新のイベントがポストされる。
  • 内部的にはURL毎にインスタンスが共有される(例えば URLを "http://xcatsan.com" として3回インスタンスを取得した場合、いずれも同じインスタンスが返る)。
  • 到達不可能なURLは使えない。URLは名前解決できてIP的に到達可能である必要がある。
  • スレッドセーフ



インストール


ソースコードの追加およびフレームワークの追加が必要。

ソースコード追加

GitHubからプロジェクトをダウンロードした後 Xcode 4 で開き、その中から下記のファイルを自分のプロジェクトへコピーして追加する。
FBNetworkReachability.h
FBNetworkReachability.m

フレームワーク追加

SystemConfiguration.framework を追加する。



ソース解説


解説は過去のブログが詳しいのでそちらを参照のこと。

Cocoaの日々: ネットワーク接続状況を知る

Cocoaの日々: ネットワーク接続状況を知る[2] SCNetworkReachabilityGetFlags はブロックする

以前ブログで紹介したバージョンではインスタンス作成直後に connectionModeを取得すると必ず未初期化状態(FBNetworkReachableUninitialization)となっていた。これだと実用上はいろいろと困るので、未初期化状態の場合は「0.5秒待つ」処理を加えた。こんな感じ。

- (FBNetworkReachabilityConnectionMode)connectionMode
{
    FBNetworkReachabilityConnectionMode mode;
    @synchronized (self) {
        mode = connectionMode_;
    }
    if (mode == FBNetworkReachableUninitialization) {
        [[NSRunLoop currentRunLoop]
         runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
    }
    return connectionMode_;
}
SCNetworkReachability における非同期処理(コールバック)はランループを使っている。接続状況変化のイベントはランループを介して SCNetworkRechability へ届くので、sleep でスレッドを止めるのではなく、runUntileDate: を使ってその場でランループの処理だけを回してやる。大抵はこの 0.5秒間の間にイベントが届き、コールバックが呼び出され、connectionMode が FBNetworkReachableUninitialization 以外になる。これによってインスタンス作成直後であっても有効な connectionMode が得られる。

(2011-11-14追記)
Cocoaの日々: [iOS] SCNetworkReachabilityGetFlags のブロックの件
上記で記事で解説したように遅延は解消されたので最新版では「0.5秒待つ」処理は入っていない。


動作確認用アプリケーション


GitHub のプロジェクトには動作確認用のアプリケーションが付属している。
接続方式に変化があるとテーブルが更新される。



カスタマイズ


特になし。強いて言えばソース解説で説明した初回の待ち時間(0.5秒)。これはソースコードを直接書き換える。



ライセンス


MIT ライセンスで、商用・非商用を問わず利用可能。カスタマイズして再配布も自由。連絡も不要。



関連情報


なし

[iOS] UINib を使ったカスタム UITableViewCell の作り方

2011年5月22日日曜日 | Published in | 2 コメント

このエントリーをはてなブックマークに追加

2011-06-09 追記 UITableViewCell の Identifier 設定を忘れてたので追記しました。

UINib を使うと簡単に Nib で定義した UITableViewCell が使える。
今回のサンプル:


[関連] Cocoaの日々: [iOS] UINib を使ったカスタム UITableViewCell の作り方(その2)ボタンの処理


実装


まず CustomCell.xib を作る。

ここで表示したいセルのデザインを行う。UITableViewCell のクラスに "CustomCell" を指定しておく。

(2011-06-09 追記)
Identifier には "CustomCell" を設定しておく。この文字列は自由に付けられるがソースコード上の該当箇所と一致させておく。設定しないとセルの再利用が行われない。


次に CustomCell クラスの実装(*.m/*.h)を作成する。こんな感じ。
CustomCell.h
@interface CustomCell : UITableViewCell {
    
}
@property (nonatomic, retain) IBOutlet UILabel* nameLabel;
@property (nonatomic, retain) IBOutlet UILabel* dateLabel;
@property (nonatomic, retain) IBOutlet UILabel* descLabel;
@property (nonatomic, retain) IBOutlet UIImageView* imageView;

@end

CustomCell.m
@implementation CustomCell
@synthesize nameLabel;
@synthesize dateLabel;
@synthesize descLabel;
@synthesize imageView;

- (void)dealloc
{
    self.nameLabel = nil;
    self.dateLabel = nil;
    self.descLabel = nil;
    self.imageView = nil;
    [super dealloc];
}

@end
今回は特別な実装はやらないので単にプロパティ値のプレースホルダとして働く。

再び xib ファイルへ戻り、アウトレットの接続を行う。

セルの準備ができたら今度は UITableView の Datasource/Delegate の実装。

RootViewController.m
#define CUSTOM_CELL_NIB @"CustomCell"

- (UITableViewCell *)tableView:(UITableView *)tableView
   cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CustomCell* cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:CUSTOM_CELL_NIB];
    if (cell == nil) {
        UINib* nib = [UINib nibWithNibName:CUSTOM_CELL_NIB bundle:nil];
        NSArray* array = [nib instantiateWithOwner:nil options:nil];
        cell = [array objectAtIndex:0];
    }

    // Configure the cell.
    int r = rand();
    cell.nameLabel.text = [NSString stringWithFormat:@"NAME-%d", r];
    cell.dateLabel.text = [[NSDate dateWithTimeIntervalSince1970:r] description];
    cell.descLabel.text = @"Permission is hereby granted, free of charge, to any
 person obtaining a copy of this software and associated documentation files ....";
    cell.imageView.image = [UIImage imageNamed:
       [NSString stringWithFormat:@"image%02ds.jpg", (r%8)+1]];
    NSLog(@"%@", [NSString stringWithFormat:@"image%02ds.jpg", (r%8)+1]);
    return cell;
}
ポイントは UINib を使っているところ。
UINib* nib = [UINib nibWithNibName:CUSTOM_CELL_NIB bundle:nil];
        NSArray* array = [nib instantiateWithOwner:nil options:nil];
        cell = [array objectAtIndex:0];
nibwithNibName:bundle: で xib ファイルを読み込み、instantiateWithOwner:options: でインスタンス化する。この時、owner は使わないので指定しない(nil)。今回の用途ではこれでも問題ない。CustomCell クラスのインスタンスを得るのには決め打ちで objectAtIndex:0 としている。もし xib に複数のトップレベルオブジェクトが存在する場合は区別する必要がある。なお UINib は xib ファイルをキャッシュするので繰り返し使っても読み込み自体は大きなオーバーヘッドにならない。以前のカスタムセル作成には UIViewController を使った方法があったが、それに比べると余計なインスタンス(UIViewController)を作らず、キャッシュも効くのでこちらのほうが軽量だと思われる。

なお CustomCell の高さは通常よりも大きくしてあるのでデリゲートを実装して置く必要がある。
- (void)viewDidLoad
{
    self.title = @"Custom Cell Sample";
    
    // calc height of custom cell
    UINib* nib = [UINib nibWithNibName:CUSTOM_CELL_NIB bundle:nil];
    NSArray* array = [nib instantiateWithOwner:nil options:nil];
    CustomCell* cell = [array objectAtIndex:0];
    cellHeight_ = cell.frame.size.height;
    [super viewDidLoad];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return cellHeight_;
}
今回は viewDidLoad でセルの高さをメンバ変数へ取っておく方法をとった。あらかじめセルの高さが固定であればソースコード上に直接数値を指定して返しても良い。


ソースコード


GitHub からどうぞ。
CustomCellSample at 2011-06-09 from xcatsan/iOS-Sample-Code - GitHub


関連情報


カスタムセルシリーズ

Cocoaの日々: [iOS] UINib を使ったカスタム UITableViewCell の作り方(その2)ボタンの処理

Cocoaの日々: [iOS] UINib を使ったカスタム UITableViewCell の作り方(その3)ボタンの処理[改良版]

スライド実装

Twitter公式アプリなどで実装されているセルのスライドアニメーションの実装など。
Cocoaの日々: [iOS] UITableView でセルをスワイプするとスライドするユーザインタフェースを実装

[iOS] 非同期処理を同期処理に変える

2011年5月16日月曜日 | Published in | 2 コメント

このエントリーをはてなブックマークに追加

問題


この前リリースしたカスタム UIAlertView は表示メソッドを呼び出した後はそのまま処理が続行される。
Cocoaの日々: [iOS] ユーザ名/パスワードの入力ができるカスタム UIAlertView ライブラリを公開

例えば下記のような場合。
- (IBAction)showAlert:(id)sender
{
    [LKAccountPanel showWithTitle:@"Test"
                       completion:^(BOOL result, NSString* username, NSString* password) {
                           NSLog(@"result: %d\nusername: %@\npassword: %@",
                                 result, username, password);
                       }];
    NSLog(@"done");
}
UIAlertView が表示された直後に "done" がログに出力される。つまり showWithTitle:.. メソッドはその場でブロックされない非同期処理になっている。これは UIAlertView の動作がデリゲートを使った非同期モデルだから。

でもアカウント入力用途で使う場合、これだと使い勝手が悪い。UIAlertView が表示され、ユーザがアクションを起こす(OK or Cancel)まではその場でブロックしていて欲しい。言い換えると非同期ではなく同期的に処理したい。非同期を同期にするにはどうしたらよいか?


NSRunLoop


UIAlertView のこの非同期な仕組みは NSRunLoop を使っている。UIAlertView を show で表示した直後のコードはブロックされることなく実行される。先ほどの例での "done"表示がこれにあたる。その後は NSRunLoop が UIAlertView の描画タイミングの制御やタッチイベントの制御(受付と配布)を行う。OK ボタンが押されると NSRunLoop を経由してタッチイベントが UIAlertView のインスタンスへ渡され、その後デリゲートメソッド alertView:clickedButtonAtIndex: が呼び出される。

このような現在の動作を、表示後に処理をブロックするようにして、OK ボタンが押された後に処理を再開させるにはどうしたらよいか?先程の例でいうと、UIAlertView で表示後に処理を一時停止させる。OK ボタンが押されて UIAlertView が消えた後、処理を再開させログに "done" と書きだすようにしたい。

これを実現するには UIAlertView の show メソッドを呼び出した後に、処理が終了するまでブロックすれば良い。
@property (nonatomic, assign) BOOL finished;
上記プロパティを追加して while で終了待ちループを構成する。
[self.alertView show];

self.finished = NO;
while (!finished) {
 ...
}
OK / キャンセル ボタンが押された時にこのプロパティを YES にしてやる。
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
        :
    self.finished = YES;
}

ただこれだと while が終了することなく回り続けてしまい、NSRunLoop へ処理が返されない。その結果、UIAlertView は表示のタイミングを得ることなく、画面には表示されない。もちろんイベントの処理もできない(ボタンが表示されないのはもちろん、イベント自体も NSRunLoop に制御が戻らない限り処理されない)。

そこで while 内で一旦 NSRunLoop へ制御を戻してやる。戻すには -[NSRunLoop runUntilDate:] を使う。このメソッドは引数で指定した日時までの間、その場で処理を一時停止し NSRunLoop へ制御を戻す。そして指定の日時がきたら処理を再開する。
NSRunLoop Class Reference

先ほどのコードにこんな感じで追加してやる。
self.finished = NO;
    while (!self.finished) {
        [[NSRunLoop currentRunLoop]
         runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
    }
すると while ループ内でおおよそ 0.5秒間 NSRunLoop へ制御が戻る。この間、while内の処理は停止する。NSRunLoop へ制御が戻れば UIAlertView が描画をして、ボタンが押された時にタッチイベントを受け取ることができるようになる。その結果、UIAlertView をあたかも(処理を一旦停止するという意味で)同期的な処理として扱うことができるようになる。なお指定秒は長すぎると while ループを抜けるまでに時間がかかるので、処理の続行までの遅れが大きくなる。一方短すぎると while ループを回す回数が増えるので無駄が多い。感覚的には 1秒では長すぎ、0.1秒では(なんとなく)無駄。その間を適当にとって 0.5秒とした。


LKAccountPanel の改良


LKAccoutPanel に上記の仕組みを導入した。
lakesoft/LKAccountPanel - GitHub

非同期式の +showWithTitle:completion: に加え、同期式の +showWithTitle:username:password: を追加した。
+ (BOOL)showWithTitle:(NSString*)title username:(NSString**)username password:(NSString**)password;
使い方はこんな感じ。
- (IBAction)showAlert2:(id)sender
{
    NSString* username;
    NSString* password;
    
    BOOL result = [LKAccountPanel showWithTitle:@"Test2"
                                       username:&username
                                       password:&password];
    NSLog(@"result: %d\nusername: %@\npassword: %@",
          result, username, password);
}
引数 username, password は結果を受け取る為にポインタのポインタを渡す。上記の場合、NSLog(@"result:.."); の処理が実行されるのは UIAlertView が閉じた後になる。

付属のサンプルではボタンを非同期式(Async)と同期式(Sync)の2つを用意して違いが分かるようにしてみた。
サンプルアプリケーションを実行し、非同期式ボタン(Async)をタッチすると +showWithTitle:completion: 直後に書かれている NSLog(@"done1"); が実行される。一方、同期式ボタン(Sync)をタッチすると +showWithTitle:username:password: の直後の NSLog(@"done2"); は UIAlertView が閉じてから実行されるのがわかる。

- - - -

UIAlertView に限らず非同期=>同期変換に runUntilDate: は使えそう。

[iOS] 簡易 Keychain Services ラッパー LKKeychain を公開

2011年5月14日土曜日 | Published in | 0 コメント

このエントリーをはてなブックマークに追加

Keychain Serivce の簡易ラッパー LKKeychain を公開しました。
lakesoft/LKKeychain - GitHub

パスワードの追加、更新、削除が行えます。追加はこんな感じ。
[LKKeychain updatePassowrd:@"hdsI3823Khdf"
                    account:@"hashiguchi@lakesoft.jp"
                    service:@"SampleService"];


インストール


GitHubからプロジェクトをダウンロードした後 Xcode で開く。

その中から LKKeychain.h と LKKeychain.m を自分のプロジェクトへコピーして追加する。


使い方


用意されているクラスメソッドを呼び出す。

追加・更新

[LKKeychain updatePassowrd:@"hdsI3823Khdf"
                   account:@"hashiguchi@lakesoft.jp"
                   service:@"SampleService"];
該当するアカウント情報がない場合は新規登録、既に存在する場合はパスワードが更新される。なお Keychain Services においてはサービス名(service)とアカウント(account)の組が検索時のキーとなる(2つが同じなら同じ情報とみなされる)。

削除

[LKKeychain deletePasswordWithAccount:@"hashiguchi@lakesoft.jp"
                              service:@"SampleService"];

デバッグ

NSArray* accounts = [LKKeychain getItemsWithServices:@"SampleService"];
指定したサービスに登録されているすべてのパスワードを配列で戻す。
(
{
    acct = "hashiguchi@lakesoft.jp";
    agrp = test;
    cdat = "0023-05-13 14:40:33 +0000";
    mdat = "0023-05-13 14:40:33 +0000";
    pdmn = ak;
    svce = SampleService;
    "v_Data" = <70617373 776f7264>;
},
:


カスタマイズ


なし。


サンプル


Github には簡単なサンプルアプリケーションが同梱されている。実行すると登録や削除、内容の確認が行える。


ソース解説


特別なことはやっていない。メソッドや定数の意味等は以前解説したのでそちらを参照のこと。
Cocoaの日々: [iOS] Keychain Services とは


ライセンス


MIT ライセンスです。商用・非商用を問わず利用可能。カスタマイズしての再配布も自由。連絡も不要(でもくれるとうれしい)。


関連情報


Cocoaの日々: [iOS] Keychain Services とは

Cocoaの日々: [iOS] 複数アプリケーション間でのデータ共有 〜 Keychain Services を使った第三の方法

(旧) Cocoaの日々: Keychain Services 調査 (1) 情報収集

[iOS] ユーザ名/パスワードの入力ができるカスタム UIAlertView ライブラリを公開

2011年5月11日水曜日 | Published in | 0 コメント

このエントリーをはてなブックマークに追加

ユーザ名とパスワードの入力を促すカスタム UIAlertView を公開しました。
lakesoft/LKAccountPanel - GitHub



呼び出しコードはこんな感じ。
[LKAccountPanel showWithTitle:@"Test"
                       completion:^(BOOL result, NSString* username, NSString* password) {
                           NSLog(@"result: %d\nusername: %@\npassword: %@",
                                 result, username, password);
                       }];
結果を Blocks で処理できるようにしてみた。

[2011-05-17 追記] 非同期用のメソッドを追加しました。
BOOL result = [LKAccountPanel showWithTitle:@"Test2"
                                       username:&username
                                       password:&password];
    NSLog(@"done2");
    NSLog(@"result2: %d\nusername: %@\npassword: %@",
          result, username, password);

参考情報:Cocoaの日々: [iOS] 非同期処理を同期処理に変える



インストール


GitHubからプロジェクトをダウンロードした後 Xcode 4 で開く。

その中から LKAccountPanel.h と LKAccountPanel.m、そして LKAccountPanel.strings を自分のプロジェクトへコピーして追加する。


使い方


クラスメソッド showWithTitle:completion: を呼び出すだけ。
(例)[LKAccountPanel showWithTitle:@"タイトル"
                       completion:^(BOOL result, NSString* username, NSString* password) {
                                   if (result) {
                                       // OKボタンが押された場合の処理
                                   }
                       }];
OKボタンが押された場合は、result == YESとなる。この時入力値が username, password に格納されて渡される。いわゆる必須チェックは行っていないので未入力でもOKボタンが押せる。未入力やキャンセル時には username と password には nil が入る。


カスタマイズ


ボタンの文言や、UITextField のプレースホルダに表示する文字列は LKAccountPanel.strings で定義している。
"OK" = "OK";
"Cancel" = "キャンセル";
"Username" = "ユーザ名";
"Password" = "パスワード";
他の文言に変更したい場合はここを変える。現在は日本語と英語の文字列を用意しているが必要なら他の言語の追加もできる。


ソース解説


定義はこう。
@interface LKAccountPanel :
 NSObject  {

}
@property (nonatomic, retain) UITextField* usernameTextField;
@property (nonatomic, retain) UITextField* passwordTextField;

// API
+ (void)showWithTitle:(NSString*)title completion:(void(^)(
  BOOL result, NSString* username, NSString* password))completion;

@end
UIAlertViewDelegate を実装してこのクラスで UIAlertViewに関する処理を完結させている。

シングルトンパターンを使っていて、showWithTitle:completion: を呼び出すと内部的には1つのインスタンスを使い回し、このインスタンスで実際の処理を行わせる。
+ (void)showWithTitle:(NSString*)title
  completion:(void(^)(BOOL result, NSString* username, NSString* password))completion
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        accountPanel_ = [[LKAccountPanel alloc] init];
    });
    
    [accountPanel_ _showWithTitle:title completion:completion];
}
シングルトンの初期化には dispatch_once を使っている。
[参考] Cocoaの日々: [iOS][Mac] dispatch_once を使ったシングルトン

UIAlertView のカスタマイズと表示は下請けのメソッド _showWithTitle:completion: でやる。
- (void)_showWithTitle:(NSString*)title
  completion:(void(^)(BOOL result, NSString* username, NSString* password))completion;
{
    self.completionBlock = completion;
    self.alertView =
        [[[UIAlertView alloc] initWithTitle:title
                                    message:@"\n\n\n"
                                   delegate:self 
                          cancelButtonTitle:NSLocalizedStringFromTable(
                              @"Cancel", LOCALIZED_STRING_TABLE, nil)
                          otherButtonTitles:NSLocalizedStringFromTable(
                              @"OK", LOCALIZED_STRING_TABLE, nil), nil] autorelease];

    LKAccountPanelBackgroundView* backgroundView =
        [[[LKAccountPanelBackgroundView alloc]
          initWithFrame:CGRectMake(15.0, 47.0, 255.0, 65)] autorelease];
    [self.alertView addSubview:backgroundView];
    
    self.usernameTextField = [[[UITextField alloc] initWithFrame:
                               CGRectMake(20.0, 52.0, 245.0, 30.0)] autorelease];
    self.usernameTextField.placeholder =
         NSLocalizedStringFromTable(@"Username", LOCALIZED_STRING_TABLE, nil);
    self.usernameTextField.keyboardType = UIKeyboardTypeEmailAddress;
    self.usernameTextField.autocapitalizationType = UITextAutocapitalizationTypeNone;
    self.usernameTextField.returnKeyType = UIReturnKeyNext;
    self.usernameTextField.delegate = self;
    [self.alertView addSubview:self.usernameTextField];

    self.passwordTextField = [[[UITextField alloc] initWithFrame:
                               CGRectMake(20.0, 87.0, 245.0, 30.0)] autorelease];
    self.passwordTextField.placeholder =
       NSLocalizedStringFromTable(@"Password", LOCALIZED_STRING_TABLE, nil);
    self.passwordTextField.keyboardType = UIKeyboardTypeASCIICapable;
    self.passwordTextField.autocapitalizationType = UITextAutocapitalizationTypeNone;
    self.passwordTextField.secureTextEntry = YES;
    self.passwordTextField.returnKeyType = UIReturnKeyDone;
    self.passwordTextField.delegate = self;
    [self.alertView addSubview:self.passwordTextField];

    [self.alertView show];
    [self.usernameTextField becomeFirstResponder];
}
・UITextFiled を2つ作って UIAlertView へ貼りつけている。
・場所を確保するために改行を3つ入れている。
・ローカライズ文字列の取得には NSLocalizedStringFromTable() を使用している。
と、あんまりたいしたことはやってない。UIAlertView のインスタンスを保持しておくのはキーボードで "Done"が押された時に閉じたいから。
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    if (textField == self.usernameTextField) {
        [self.passwordTextField becomeFirstResponder];
    } else if (textField == self.passwordTextField) {
        [self.alertView dismissWithClickedButtonIndex:1 animated:YES];
        [self alertView:self.alertView clickedButtonAtIndex:1];
    }
    return YES;
}
-[UIAlertView dismissWithClickedButtonIndex:animated:] は何故かデリゲートメソッド alertView:clickedButtonAtIndex: を呼び出してくれなかったので明示的に呼び出すようにした。

なお Blocks もプロパティとして定義することができる。
@property (nonatomic, retain copy)
   void(^completionBlock)(BOOL result, NSString* username, NSString* password);
こうやっておくと retain の手間が省けるし release も楽。

UITextFiled の背景用に専用の UIView を用意している。
@interface LKAccountPanelBackgroundView : UIView {
}
@end
drawRect: で UIBezierPath を使い角丸矩形と横線、そして白い背景を描く。
@implementation LKAccountPanelBackgroundView

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor clearColor];
    }
    return self;
}

- (void)drawRect:(CGRect)rect
{
    UIBezierPath* path = [UIBezierPath bezierPathWithRoundedRect:self.bounds
                                                    cornerRadius:5.0];
    [path moveToPoint:CGPointMake(0, self.bounds.size.height/2.0)];
    [path addLineToPoint:CGPointMake(self.bounds.size.width-1.0,
                                     self.bounds.size.height/2.0)];
    [[UIColor whiteColor] set];
    [path fill];
    [[UIColor lightGrayColor] set];
    [path stroke];
}
@end
UIBezierPath が追加されてほんと楽だ。


ライセンス


MIT ライセンスです。商用・非商用を問わず利用可能。カスタマイズしての再配布も自由。連絡も不要(でもくれるとうれしい)。

[iOS][Mac] dispatch_once を使ったシングルトン

2011年5月9日月曜日 | Published in | 0 コメント

このエントリーをはてなブックマークに追加

[元ネタ] Cocoa Samurai: Singletons: You're doing them wrong

GCD の dispatch_once 関数を使うとスレッドセーフなシングルトンの初期化処理をこんな感じで書ける。
static LKLocationManager* sharedManager_ = nil;
+ (LKLocationManager*)sharedManager
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedManager_ = [[LKLocationManager alloc] init];
    });
    return sharedManager_;
}
dispatch_once() の第2引数のブロックは1回のみ実行されることが保証される。

"man dispatch_once" で見ると "Well designed code hides the use of lazy initialization." と書いてあり、まさにシングルトンの初期化にうってつけの関数。
Xcode 4 では dispatch_once の Code Snipet が用意されていて dispatch_on... と入力するとプルダウンが出て選択すると雛形が展開される。
雛形はこんな感じ。

これは便利。
今後はこれを使っていこう。

人気の投稿(過去 30日間)