思い立ったが吉日!

iOSが好きです。

GCDの用語整理

GCDの用語整理

GCDのディスパッチキューには

  • 直列
    • DISPATCH_QUEUE_SERIAL
  • 並列
    • DISPATCH_QUEUE_PRIORITY_HIGH
    • DISPATCH_QUEUE_PRIORITY_DEFAULT
    • DISPATCH_QUEUE_PRIORITY_LOW
    • DISPATCH_QUEUE_PRIORITY_BACKGROUND

がある

また、それぞれ処理に同期(sync)と非同期(async)がある

  • 直列はin FIFO orderで、ディスパッチ内部は先入れ先出しで順々に処理されていく。

  • 並列はdisk I/O is throttledで順序はバラバラ。

  • 同期は処理の完了を待ってから次の処理に移る。

  • 非同期は処理の完了を待たずに次の処理を始め、その完了は各々に干渉しない。

したがって、パターンとして

  • 直列 同期
  • 直列 非同期
  • 並列 同期
  • 並列 非同期 がある。

ややこしいですが、mixiさんの研修資料に実行結果が載ってますので、参考にすると良さそうです。

https://github.com/mixi-inc/iOSTraining/wiki/8.2-Grand-Central-Dispatch

Blocks

Objective-CのBlocksのまとめ

といっても全部まとめるとややこしいので、よく使われる関数の引数にBlocksを渡して、コールバックを受け取るという形のみに絞ってまとめます。

とわいえ基本形の復讐

^(戻り値の型)(引数リスト){式}

ところが、関数の引数になるとやや形が変わる

(戻り値の型)(^)(引数リスト)ブロック名

引数なし、戻り値なしのBlocksを引数に取るメソッド

- (void)hoge:(void(^)(void))completion
{
    // 何か処理

    // Blocksの式の部分を実行    
    completion();
}

// メソッドの実行

[self hoge:^{
    // 式の部分
}];

Blocksに引数があるパターン

何か非同期処理をする際に、その非同期処理に対して引数を渡すことは多々ある

-(void) hogehoge:(void(^)(int,int))completion
{
    // 何か処理A
    // 5秒止めてLogを出す
    sleep(5);
    completion(20,10);
    // 何か処理B
}

// メソッドの実行

// 何か処理C
[self hogehoge:^(int x, int y) {
        NSLog(@"%d",x); // 20
        NSLog(@"%d",y); //10
}];
// 何か処理D

処理順は C -> A -> B -> D

メソッドの引数が2つ(StringとBlocks)で、そのBlocksの引数も2つ

-(void)hogehogehoge:(NSString*)str completionHandler:(void(^)(NSString* p,NSString* q))completion
{
    NSString *strings = [str stringByAppendingString:@"world"];
    NSString *ex = [strings stringByAppendingString:@"!!!!"];
    completion(ex,strings); // pにexが、qにstringsが入る
}

// メソッドの実行

[self hogehogehogehoge:@"hello" completionHandler:^(NSString *p, NSString *q) {
        NSLog(@"%@",p);  //  helloworld!!!!
        NSLog(@"%@",q); //   helloworld
}];

UIColorのカテゴリ

以前にUIColorのカテゴリを書いた際に、Hex文字列をUIColorで返すカテゴリを作っていたが、Alphaを16進数ではなく%で送りたい場合のほうが多い気がしたので最近ではこちらを使っている。

Alphaだけ%指定
+ (UIColor*)colorWithHex:(uint32_t)hex alpha:(float)alpha
{
    CGFloat red = ((hex & 0xff0000) >> 16) / 255.0f;
    CGFloat green = ((hex & 0x00ff00) >> 8) / 255.0f;
    CGFloat blue = ((hex & 0x0000ff)) / 255.0f;
    CGFloat pAlpha = alpha / 100.0f;
    return [UIColor colorWithRed:red green:green blue:blue alpha:pAlpha];
}

+ (UIColor*)colorWithHexString:(NSString*)string alpha:(float)alpha
{
    uint32_t hex = 0x0;
    NSScanner *scanner = [NSScanner scannerWithString:string];

    [scanner scanHexInt:&hex];

    return [UIColor colorWithHex:hex alpha:alpha];
}

プリプロセッサ

プリプロセッサ

iOS開発においてもC言語プリプロセッサの機能が使われることが多々ある。

プリプロセッサは、C言語の機能の一つで、C言語の拡張であるObjective-Cでも、Swiftでも使うことができるため、iOS開発者はこれからも使い続けるであろう機能である。

プリプロセッサには#付くのが特徴で、大文字表記、単語区切りは_とするのが通例である。

コンパイル前にプロセッサに命令できるので次のようなことが可能になる。

  • ファイルの読込み
  • 定数、マクロの定義
  • 条件付コンパイル

ファイルの読込みにおいて、デバック時と本番時で読み込むソースを分ける

#ifdef DEBUG
    import "ForDebugView.h"
#else
    import "HogeView.h"
#endif

定数、マクロの定義として

#define SCREEN_BOUNDS   ([UIScreen mainScreen].bounds)

のように、慣れると結構便利な機能なので積極的に使っていきたい。

参考

www.itsenka.com

iOS - Objective-Cでよく使う便利マクロを10個集めてみた - Qiita

qiita.com

メソッド引数のポインタ

メソッド引数のポインタ

Objective-Cを書いているとメソッドの引数に

+(void)setMessageElement:(TBXMLElement *)element toMessage:(Message**)aMsg 
{
[*aMsg setTest:[TBXML textForElement:test]];
}
[self setMessageElement:msgsElement toMessage:&msg];

このように**や&が付いている場合がある。

この**や&はなんだろう?と調べるとポインタを引数に渡しているようである。

関数定義側が**の引数を取るときは、呼び出す側は&で渡す必要があるらしい。

ポインタを渡すと渡した変数の値がメソッド内で変更された時、元の変数の値も変更されているということらしい。

値渡しと参照渡しの違いのようである。

C言語ベースで値渡しと参照渡しの挙動の違いを例示していく。

渡ってきた値を入れ替えるメソッド

値渡し

void swap(int x, int y)
{
    int tmp;
    tmp = x;
    x = y;
    y = tmp;
}

int num1 = 10;
int num2 = 5;

hogehoge.swap(num1,num2);

printf(num1);     // 10
printf(num2);    //   5

元の値は変化していない

参照渡し

void swap(int *pX, int *pY)
{
    int tmp;
    tmp = *pX;
    x = *pY;
    y = tmp;
}

hogehoge.swap(num1,num2);

printf(num1);     // 5
printf(num2);    //  10

元の値が変更されている

つまり最初の例で示したポインタを渡すメソッドは、参照渡しとなっており、&msgはメソッド内で値が変更されたら、呼び出し側のmsgの値も変更されている。

パフォーマンス改善の意味

パフォーマンス改善の意味

単純なアプリ作成を卒業し、ある程度規模の大きなアプリ開発をしていると並列処理やメモリ解放など、パフォーマンス改善の問題が結構出てくる。

アイディアが実現できればいい、動けばいい、表示に間違いがなければOKと言った作りで満足しないように、下記のことを頭の隅において、しっかりパフォーマンス改善の知識も蓄えていきたい。

サイトパフォーマンスの最適化 | SEO 検索エンジン最適化

このページによると

  • ページの表示が1秒遅れると
  • 成約が7%減少する
  • ページビューが11%低下する
  • 顧客満足度が16%減退する
  • ユーザーのうち57%は、ページの読み込みが3秒を超えると離脱する
  • そのうち80%のユーザーは二度と戻ってこない
  • さらにそのうちの半分は、自分のネガティブな体験について他の人々に伝える

とあります。

WEBページに対する調査なので単純に比較することは出来ませんが、このようにユーザを待たせると大きな機会損失に繋がるようです。

NSOperationQueue

並列処理

iOSで並列処理といえばGCDとNSOperation/NSOperationQueueの2種類があります。 GCDはCベース、NSOperation/NSOperationQueueはObjective-CベースのAPIです。

直列で処理を実行していると、先の重い処理が終わらないとUIの更新が行われず、ユーザを待たせることにつながってしまいます。

今回はNSOperation/NSOperationQueueの方を使って、サイトから文字列を取得し、それをUITextViewに表示するサンプルを紹介します。

並列処理

NSOperationQueue* queue = [NSOperationQueue new];
[queue addOperationWithBlock:^{
        // 重い処理(URLにアクセスしそこから文字列を取得)
        NSURL* url = [NSURL URLWithString:@"http://www.apple.com/jp/ipad/"];
        NSString* response = [NSString stringWithContentsOfURL:url usedEncoding:nil error:nil];

        // UIの更新(必ずmainから行う)
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        self.textView.text = response;
    }];
}];