2011-01-31

『iOS 4プログラミングブック』 第5章マルチスレッド 補遺 まとめ



2011年1月27日発売の『iOS 4プログラミングブック』。このうち

■第5章 マルチスレッド - Blocks、Grand Central Dispatch の詳解。57ページ。
■第10章 ユニバーサル対応 - ユニバーサルアプリケーションについて。15ページ。

を書かせていただきました。

この「第5章 マルチスレッド」にて、ちょっとAdvanced過ぎるかも、と思った内容を補遺として連載しています。


__block修飾子」について
『iOS 4プログラミングブック』 第5章マルチスレッド 補遺 その1
『iOS 4プログラミングブック』 第5章マルチスレッド 補遺 その2
『iOS 4プログラミングブック』 第5章マルチスレッド 補遺 その3
『iOS 4プログラミングブック』 第5章マルチスレッド 補遺 その4
『iOS 4プログラミングブック』 第5章マルチスレッド 補遺 その5



サンプルコード」について
『iOS 4プログラミングブック』 第5章マルチスレッド 補遺 その6



Blocks」について
『iOS 4プログラミングブック』 第5章マルチスレッド 補遺 その7



本の間違いについて
『iOS 4プログラミングブック』 第5章マルチスレッド 補遺 その8




『iOS 4プログラミングブック』 第5章マルチスレッド 補遺 その5

そろそろ皆様のお手元にもあるのではないかと思ったりしております。『iOS 4プログラミングブック』。

__block変数の書き換えは、スレッドセーフではない

さて、マルチスレッド下で安全に__block変数を書き換える方法とは。

そもそもマルチスレッドにするためにGCDのGlobal Queueを使ってるわけですから、やはりここはDispatch Semaphoreを使っておきましょう。詳しくはワシの226ページに書いてあーる。


#import <dispatch/dispatch.h>
#import <stdio.h>

int main()
{
__block int total = 0;

dispatch_semaphore_t sema = dispatch_semaphore_create(1);
dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10000, queue, ^(size_t index) {

dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);

++total;

dispatch_semaphore_signal(sema);

});
dispatch_release(sema);

printf("total=%d\n", total);

return 0;
}


ふつうにセマフォです。


あと、今回の場合、Atomic operationsが使えます。

http://developer.apple.com/library/ios/documentation/System/Conceptual/ManPages_iPhoneOS/man3/atomic.3.html


#import <libkern/OSAtomic.h>



dispatch_apply(10000, queue, ^(size_t index) {

OSAtomicIncrement32(&total);

});


Barrierあり/なしの使い分けは、http://stackoverflow.com/questions/2436513/macosx-osatomic-vs-osatomicbarrierにありました。


もしくはGCC組み込みAtomic関数(http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html)を使うのも手です。


dispatch_apply(10000, queue, ^(size_t index) {

__sync_fetch_and_add(&total, 1);

});


clangでも使えます。Mac OS X、iOS以外の環境でも使える点がメリットでしょうか。また上記のとおり、OSAtomicに対して若干汎用的すぎるところがデメリットと言えるかと(++がないとか)。移植性を取るかどうか、てことですね。GCC拡張使って移植性どうこう言うのもあれだとは思いますが。

てことで、__block修飾子に関しては、以上で終りです。補遺はまだ続くかもしれません。

2011-01-28

『iOS 4プログラミングブック』 第5章マルチスレッド 補遺 その4

そろそろ皆様のお手元にもあるのではないかと思ったりする『iOS 4プログラミングブック』。

「しかし、第5章『マルチスレッド』の補遺の割に、ちっともマルチスレッドじゃない」

とお嘆きの読者様。朗報です。やっとマルチスレッドの話です。


__block変数への書き込みってスレッドセーフ??


GCDのdispatch_applyで、Global QueueにBlock突っ込むと、それはもう簡単に複数スレッドから同時に__block変数を書き換えることができます。


... あ、もしかして、GCDことGrand Central Dispatch、よくご存知でなかったりしますでしょうか? そういう話でしたら、それはもう詳しく説明している本がありますので、この場を借りてご紹介できればな、と思います。



iOS 4プログラミングブック
■5-3 Grand Central Dispatchの概要
■5-4 Grand Central Dispatchの解説



はい、宣伝させていただいてありがとうございます。dispatch_applyでガンガン__blockを書き換えるソースを書いてみます。


#import <dispatch/dispatch.h>
#import <stdio.h>

int main()
{
__block int total = 0;

dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10000, queue, ^(size_t index) {

++total;

});

printf("total=%d\n", total);

return 0;
}


実行すれば


total=10000


と出るはずです。実行してみましょう!


total=6749


あれ。あ、ちなみにCore 2 DuoなMacBook Proで実行してますよ。もう一度。


total=10000


期待通り。念のためもう一回。


total=8664


うーん。ということで、すでにお気づきのことと思いますが、

__block変数はスレッドセーフではありません


その1から読んでくださっている読者の方には当然の結果かとおもいます。clang -rewrite-objcで見たとおり、++(total.__forwarding->total);になってるだけですから、スレッドセーフなわけがないのです。

マルチスレッド下で、__block変数を安全に更新する方法とははたして!? つづきます。

2011-01-27

『iOS 4プログラミングブック』 第5章マルチスレッド 補遺 その3

いよいよ本日、2011年1月27日発売!の『iOS 4プログラミングブック』。

その1その2と「__block修飾子」について深追いしてきましたが、まだ潜ってみます。ぶくぶくぶく。


潜る前に、まずは地図を見ておきましょう。

179ページにあるとおり、Blocksのことが知りたかったらLLVMのドキュメントを参照します。


http://clang.llvm.org/docs/BlockLanguageSpec.txt
http://clang.llvm.org/docs/Block-ABI-Apple.txt


... しかしまぁ、言うは易く行うは難し。英語読むくらいならソースコード読むほうが楽ですね。地図なしで。


てことで、ソースを読んでみます。ぶくぶくぶく。

Block_copyとかBlock_releaseとかは、C言語のランタイムライブラリの一部としてLLVMのソースツリーに入ってます。

https://llvm.org/svn/llvm-project/compiler-rt/trunk/BlocksRuntime/Block_private.h
https://llvm.org/svn/llvm-project/compiler-rt/trunk/BlocksRuntime/runtime.c

このへん。

その2で出てきたflags: 0x1000002のbit定義がありますね。


enum {
BLOCK_REFCOUNT_MASK = (0xffff),
BLOCK_NEEDS_FREE = (1 << 24),
BLOCK_HAS_COPY_DISPOSE = (1 << 25),
BLOCK_HAS_CTOR = (1 << 26), /* Helpers have C++ code. */
BLOCK_IS_GC = (1 << 27),
BLOCK_IS_GLOBAL = (1 << 28),
BLOCK_HAS_DESCRIPTOR = (1 << 29)
};


BLOCK_REFCOUNT_MASK = 0xffff... 65536回Block_copyするとやばそうですね!


それはさておきBlock_copyすると、__block変数は_Block_byref_assign_copy()でcopyされます。


/*
* Runtime entry points for maintaining the sharing knowledge of byref data blocks.
*
* A closure has been copied and its fixup routine is asking us to fix up the reference to the shared byref data
* Closures that aren't copied must still work, so everyone always accesses variables after dereferencing the forwarding ptr.
* We ask if the byref pointer that we know about has already been copied to the heap, and if so, increment it.
* Otherwise we need to copy it and update the stack forwarding pointer
* XXX We need to account for weak/nonretained read-write barriers.
*/

static void _Block_byref_assign_copy(void *dest, const void *arg, const int flags) {



forwardingポインタに代入しているところを見ると


copy->forwarding = copy; // patch heap copy to point to itself (skip write-barrier)
src->forwarding = copy; // patch stack to point to heap copy


heap上構造体のforwardingも、stack上構造体のforwardingも、heap上の構造体を示すように書き換えられてます。安心して__block変数を操作できますね。


その2で使った秘密の_Block_byref_dump()もありますよ。


ちなみに、Mac OS X 10.5/iOS 3.1.3以前でもBlocksを使えるようにするplblocksてのがあります。

http://code.google.com/p/plblocks/source/browse/Runtime/trunk/Source/Runtime/Block.m

たとえばiOS SDK 4.2に入っているtoolchain(gcc, llvm-gcc, clang)は、Block構文をコンパイルすることが可能です。ということで実は、Deployment Targetを3.1.3にしても(Deployment Targetについては、iOS 4プログラミングブック 第10章ユニバーサル対応で詳しく説明してますよ!)、stack上でだけならBlocksを使うことができます。ただしBlocks用ランタイムがないため、Block_copy/Block_releaseなどはできません。そこで、plblocks runtimeのヘッダをインクルードして、plblocks runtimeのソースとともにコンパイルすると、plblocksのBlock_copy/Block_release実装を使えるようになるわけです。

ついでにlibdispatchを使えば、iOS3.1.3とかでもGrand Central Dispatchまで使えるわけですが、それはまた別の講釈で...

__block修飾子については語りつくした感がありますが、まだつづくよ

2011-01-26

『iOS 4プログラミングブック』 第5章マルチスレッド 補遺 その2

『iOS 4プログラミングブック』 第5章マルチスレッド 補遺 その2

2011年1月27日発売でも、すでに書店に並びつつある『iOS 4プログラミングブック』。

今回も引き続き第5章マルチスレッドの補遺として、「__block修飾子」を深追いしてみます。

その1で書いたとおり、__block変数の実態は「total.__forwarding->total」て感じです。Block生成直後はstackに居るので、この__forwardingポインタは自分が含まれる構造体を示し、Block_copyでstackからheapに移動されると(189ページ参照)、__forwardingポインタが移動後のアドレスを示すわけですね。「total.__forwarding->total」は、stackにあるか、heapにあるか、気にせずにアクセスできるわけです。


確かめてみましょう!


#import <Foundation/Foundation.h>
#import <stdio.h>

extern const char *_Block_byref_dump(void *);

void dump(int line, int *p)
{
p -= 4;
printf("\ndump line:%d\n", line);
puts(_Block_byref_dump(p));
}

int *test()
{
__block int total = 11;

dump(__LINE__, &total);

void (^block_on_stack)() = ^{

++total;

dump(__LINE__, &total);
};

block_on_stack();

printf("\n___ Block_copy ___\n");
void (^block_on_heap)() = Block_copy(block_on_stack);

dump(__LINE__, &total);

block_on_stack();

block_on_heap();

printf("\n___ Block_release ___\n");
Block_release(block_on_heap);

dump(__LINE__, &total);

block_on_stack();

return &total;
}

int main()
{
dump(__LINE__, test());
}


__block変数をダンプするための秘密の関数 _Block_byref_dump を使って、それぞれの状況で__block変数の状態を表示してみます。このソースコードに埋めてみましょう。


int *test()
{
__block int total = 11;

dump(__LINE__, &total);

dump line:17
byref data block 0xbffffa70 contents:
forwarding: 0xbffffa70 ← stackに生成されてる
flags: 0x0
size: 20


void (^block_on_stack)() = ^{

++total;

dump(__LINE__, &total);
};

block_on_stack();

dump line:23
byref data block 0xbffffa70 contents:
forwarding: 0xbffffa70 ← stack上のBlockからstack上の__block変数を使用
flags: 0x0
size: 20


printf("\n___ Block_copy ___\n");
void (^block_on_heap)() = Block_copy(block_on_stack);

dump(__LINE__, &total);

dump line:31
byref data block 0x100150 contents:
forwarding: 0x100150 ← heapにコピーされた!
flags: 0x1000002 ← 要Block_releaseフラグと、参照カウンタ(2)
size: 20


block_on_stack();

dump line:23
byref data block 0x100150 contents:
forwarding: 0x100150 ← stack上のBlockからheap上の__block変数を参照している
flags: 0x1000002
size: 20


block_on_heap();

dump line:23
byref data block 0x100150 contents:
forwarding: 0x100150 ← heap上のBlockからheap上の__block変数を使用している
flags: 0x1000002
size: 20



printf("\n___ Block_release ___\n");
Block_release(block_on_heap);

dump(__LINE__, &total);

dump line:40
byref data block 0x100150 contents:
forwarding: 0x100150
flags: 0x1000001 ← Block_releaseしたので参照カウンタが減少(2→1)
size: 20


block_on_stack();

dump line:23
byref data block 0x100150 contents:
forwarding: 0x100150 ← stack上のBlockからheap上の__block変数を使用。まだ参照カウンタが1なのでheap上に残存
flags: 0x1000001
size: 20


return &total;
}

int main()
{
dump(__LINE__, test());

dump line:49
byref data block 0x100150 contents:
forwarding: 0x100150
flags: 0x1000000 ← 参照カウンタが0なのでheap上に残ってないはず!
size: 20

}

(Mac OS X 10.6、clang -m32で確認)


てことで、Block_copy使うと、本当にstackからheapに移動してることがわかりました。

ついでに、Block_releaseしても、heapからstackに戻すわけじゃなさそうなこともわかりました。

あ、dump関数が、「total.__forwarding->total」からのアクセスなので、Block_copy以降全部heapに存在しているように見えますが、stack上のtotal.__forwardingがちゃんとheap上のものをさしているので、stack上のtotal構造体を触っても平気です。

もはや誰得の情報なのか混迷を極めて来ましたが、つづきます。

2011-01-25

『iOS 4プログラミングブック』 第5章マルチスレッド 補遺 その1

そろそろ書店に並びつつある、かもしれない、『iOS 4プログラミングブック』。

「第5章 マルチスレッド」にて、ちょっとAdvanced過ぎるかも、と思った内容を補遺として連載してっちゃいますよ。



というわけで今回は、本当に187ページで説明している「__block修飾子」について、もうちょっと掘り下げてみます。

たとえば、「__block修飾子」を付けたint型のauto変数を用意してみましょう。


int main()
{
__block int total = 11;

++total;

return total;
}


「__block」付いてる以外は、ごく普通のコードです。

しかし、コレ実際何がどうなるんでしょうか。


実は、clangを使うと一瞬でわかります。


$ clang -rewrite-objc test.m


これでObjective-Cのソースコードを、おなじみのC++のソースコードに変換することが可能なのです。

あ、C++になじみがない方もご安心を。--rewrite-objcで生成されるC++ソースコードは、ほぼC言語です(ほぼC言語なのにC++として生成されるのか、それは謎です)。


生成されたC++ソースコードを見てみると。


(抜粋)
#define __block

struct __Block_byref_total_0 {
void *__isa;
__Block_byref_total_0 *__forwarding;
int __flags;
int __size;
int total;
};

int main()
{
__block struct __Block_byref_total_0 total = {
(void*)0,
(struct __Block_byref_total_0 *)&total,
0,
sizeof(struct __Block_byref_total_0),
11
};

++(total.__forwarding->total);

return (total.__forwarding->total);
}


int型だったtotalが、なにやら複雑な構造体になっていることがわかります。そして元々のint totalは、構造体のメンバーとして収納されています。

なぜこのような構造体が必要なのか。それはiOS 4プログラミングブック 189ページで説明しているように、Block_copyによって__block変数がstackからheapに移動されてもへいちゃら! と言えるようになっているわけです。

以上、何気なく付けた__block修飾子が実はこんな構造体になってました、というお話でした。

その2に続きます。

2011-01-24

iOS 4プログラミングブック サンプルコード



今週には本屋に並ぶ『iOS 4プログラミングブック』ですが、本の内容をよりよく理解できるように、サンプルコードも準備させていただいています。

http://www.impressjapan.jp/books/2976

発売日の2011年1月27日には、上記URLよりサンプルコードにアクセスできると思います。
(2011/1/24追記: すでにサンプルコードにアクセスできます!)


私が担当した2つの章

■第5章 マルチスレッド
■第10章 ユニバーサル対応

でも、それぞれ1つづつサンプルコードを用意してます。

第5章 マルチスレッド用のサンプルコードでは、Blocks、Grand Central Dispatchをそりゃもうふんだんに使用しました。まさに実戦サンプルアプリケーションではないかと思います。しかも趣味丸出しの、まさかのcocos2d使用!です。

ソースコードは、こんな感じです。


/*
* 「5-4-2 Dispatch Queue」(213ページ)
* 各記事のフォトイメージを取得するための非同期HTTP通信は、
* メインスレッド(Main Queue)で実行される必要がある。
*/
dispatch_async(mainQueue, ^{

/*
* 非同期HTTP通信(要メインスレッド(Main Queue))
*/
[AsyncURLConnection request:imgUrl
completeBlock:^(NSData *data) {

/*
* 「5-4-2 Dispatch Queue」(213ページ)
* 各フォトイメージに対する処理は
* メインスレッド(Main Queue)で実行される必要はないので
* Global Queueで実行。
*/
dispatch_async(globalQueue, ^{

/*
* ファイル書き出しを、メインスレッドで行うと
* UI更新やイベント処理が遅延するため、
* メインスレッド以外で実行させる必要がある。
*/


iOS 4プログラミングブック』は、今週、2011年1月27日発売です。是非お手にとってみてくださいませ。

2011-01-14

Objective-Cのautorelease

Objective-Cのautoreleaseで、実際に何が起こるのか。オープンソースのCocoa実装GNUstepのソースコードで追いかけてみました。

http://togetter.com/li/88945 「autoreleaseのひみつ」

Cocoa全クラスの親クラス NSObjectにautoreleaseメソッドがあり、このautoreleaseメソッドは、NSAutoreleasePoolクラスのクラスメソッドaddObject:メソッドを呼ぶだけです。


[hoge autorelease];
/* [NSAutoreleasePool addObject:hoge]; と同じ */


NSAutoreleasePool addObject:は、そのスレッドのカレントプールにオブジェクトを追加します。メインスレッドの場合、通常NSRunLoopのループ先頭で確保されたプールになります。

NSRunLoopのループ終わりで、確保されていたプールが解放され、そのプールに入っているオブジェクト全てにreleaseが送られます。

図解すると次の通り。


(※ pool = [[NSAutoreleasePool alloc] init]; ですね)

NSAutoreleasePoolをalloc,initすると、そのプールがカレントプールになります。以下のようなスタックになってます。


(※ こちらもpool* = [[NSAutoreleasePool alloc] init]; ですね)

クラスメソッドを呼びまくったり([NSString stringほげ]とか)、autoreleaseを呼びまくるようなシチュエーション、たとえば、たくさん画像を読み込む場合。ファイルを読み込んだNSDataオブジェクト。それから作ったUIImageオブジェクト。サイズを変更したりしてまたUIImageオブジェクト。なんて時にautoreleaseしただけでreleaseされてないオブジェクトが大量に残ったりします。


for() {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

/* 画像読み込み処理 */;

[pool drain];
}


とすれば、こまめにreleaseすることができます。Mac OS Xでガベージコレクションいかしたプログラミングする時に役立つように、drainを使っておきましょう。GCされます。


メモリ管理全般の話は、Memory Management in Objective-Cがわかりやすいかと思います。iDeveloper TVのフリーセッションビデオです。Objective-Cのallocの前に、そもそもC言語のmallocで何が起きるのか、から入っています。


あ、そういえば、iPhone開発本「iOS 4プログラミングブック」(2011年1月27日発売) 344ページ中70ページくらい書いたりしています。BlocksとGrand Central Dispatchについて、本当に詳しく書きましたので、よろしければ是非。

2011-01-13

iPhoneアプリ開発講座

マディソン・エリア工芸学校(Madison Area Technical College)のiPhone開発講座がとてもとてもとても充実しています。

1項目3時間(実際の授業では1時間×3コマみたいです)に及ぶビデオがなんと14本(Springの14は3分割されてますから実際は16本)、さらに2期分で、計28本 合計84時間!

WWDC 2010のビデオと同じくらいオススメの内容です。

iTunesのリンクが正常に動作しないようで、以下のリンクをクリックしてもiTunes Uに飛べるとは限りません。iTunesでAdvanced iPhone Developmentを検索しましょう。


■Advanced iPhone Development - Spring 2010 http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=407243032
Course Notes

■Advanced iPhone Development - Fall 2010 http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=407243028


たとえば


Fall 2010 12. Multithreading, multitasking, and GCD
35分くらいマルチタスキング、1時間5分くらいみっちりスレッド。1時間10分以上Grand Central Dispatch。スレッド部分で、performSelectorInBackgroundの説明とともに、Race Conditionなどスレッドプログラミングにおける基本をみっちりやって、NSOperationQueueの使い方。そしてGrand Central Dispatchでどれだけ楽になるか。という流れ。

Spring 2010 12. OpenGL ES, Fall 2010 13. OpenGL ES 2.0
OpenGL ES 1.1、OpenGL ES 2.0を網羅。画面を確認しながらvertex shader、fragment shaderを説明。なかなか見かけない講座です。

Sprint 2010 14 Guest Speaker, Fall 2010 14. Selling iOS applications
アプリの売り方、プロモーションについて、売上の推移、AdMob,iAd。これまたなかなか見られない内容です。WWDCではこういうのは見れない(笑)



あ、英語に抵抗ある人は『iOS 4プログラミングブック』をどうぞ! Blocks、Grand Central Dispatchを知りたいならコレ

2011-01-11

MacVim-KaoriYa 20110111

MacVim-KaoriYa 20110111版をリリースしました。

http://code.google.com/p/macvim-kaoriya/

http://macvim-kaoriya.googlecode.com/files/macvim-kaoriya-20110111.dmg

MacVim-KaoriYaのMacVimメニューの「アップデートを確認」よりアップデートをお試しくださいませ。

Vim 7.3.99、MacVim Snapshot 56、香り屋パッチ 20110109ベースです。

GUI実行時(MacVim.appを実行時)のIM制御のデフォルトを変更しました(Readme 日本語入力(IM)自動オン/オフ)。

set imdisableactivate


ESCでIM入力が自動でオフになりますが、再度入力モードに入っても自動でIMをオンにしなくなりました。

set noimdisableactivate


とすると、ESCでIM入力が自動でオフになり、再度入力モードに入ると自動でIMをオンにします。

set imdisable


で、IM自動制御自体を禁止します。