「ヤ~、ホ~、FORTRAN」でググると結構ヒットします。
 みんな、通ってきた道なんだね。

 「UIKitフレームワークのクラスは基本的にメインスレッドでしか動作保証しないよ。」ってのがAppleのスタンスなわけですよ。
 なので、UIImageViewへのUIImage設定なんかも、サブスレッドでやるのはちょっとどうかな~て事になるわけで…
 ま、Appleのサンプルでサブスレッドで、UIImageViewへのUIImage設定かましてるソースがあったりするので、そこまで神経質にならんでもいいのかもしれんが、実際自分の

------------
サンプルプロジェクト:ThumbnailView-4.zip

で、試しにThumbnailViewController.mの
- (void)loadingimages
{
int tag = 1;
for (NSString* filepath in _images) {
・・・
[self performSelectorOnMainThread:@selector(setimage:)
withObject:dic waitUntilDone:NO];

}
}

- (void)loadingimages
{
int tag = 1;
for (NSString* filepath in _images) {
・・・
[self setimage:dic];
}
}
にしてサブスレッドで直接設定するようにすると、全画像のサムネイル化が完了するまで、画面上にハートが出てこないんザマスよ。やっぱ駄目じゃん。
注意)試す人はRunする前にProduct→Cleanメニュー実行しておくように。

 で、このことからもわかると思うけど、この
-(void)performSelectorOnMainThread:withObject:waitUntilDone:

てのが、前回話したスレッド間の同期処理のひとつなわけです。
 こいつは、メインスレッド上で指定したセレクタの処理を実行せよってメッセージなんですが…

 なんでそんな事ができるのか?

 そもそもメインスレッドはメインスレッドで何か処理をやってるわけですよ。
 一つのスレッドには一つの処理であって、2つの処理を同時におこないたければ、2つのスレッドで別々に実行しなきゃいけない。
 なので、当然はじかれます。

$テン*シー*シー-1
あほかワレっと

 じゃ、なんで割り込めるのかというと、iOSに限らず、MacやWindows、およそGUIをもつアプリケーションてのは、起動して終了するまでユーザーのアクションを見張って、それに対応するって作業の繰り返しをしてるわけでして…

$テン*シー*シー-2

 こいつをイベント(ユーザーのアクションとかいろいろ)駆動型とか言うわけですが、この場合、イベントキューからイベント取り出しては、その対応処理をするって構造なんで処理に切れ目があるんですよ。

$テン*シー*シー-3

 なので、イベントの一つとしてサブスレッドからの処理依頼をつっこめば、ひとつのイベント処理として対応してくれるわけです。

$テン*シー*シー-4

 実際はもうちょっと複雑な機構ですが「だいたい合ってる」て感じで理解してください。
 Appleではこの一連のイベント処理をRunLoop(日本語訳では実行ループとか訳されてる)と呼んでいてクラス(NSRunLoop)や関数(CFRunLoop)で提供しています。

$テン*シー*シー-5

 RunLoopの存在を確かめるのは簡単で、XcodeでNew→Project→New…メニューを選びEmpty Applicationテンプレートを選んで新規プロジェクトを作ったら

$テン*シー*シー-6

 こんな感じで設定して↓

$テン*シー*シー-7

 RLAppDelegate.mで以下のクラスを作ってUIWindowのかわりに使うだけです。
#import "RLAppDelegate.h"

@interface XWindow : UIWindow
@end
@implementation XWindow
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
printf("touch\n");
}
@end


@implementation RLAppDelegate
 ・・・
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[XWindow alloc] initWithFrame:
[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}

 あとはprintfにブレークポイント置いてRun。

------------
サンプルプロジェクト:RunLoop-1.zip

 画面をタップするとブレークポイントで止まるんで、その呼び出しリストを見てみるとCFRunLoopRunInModeとか、それっぽい名前が並んでるわけですよ。

$テン*シー*シー-8

 メインスレッドでは、このRunLoopが動作してるので、サブスレッドからの処理依頼に対応できるってわけです。

 ちなみに、このRunLoopはタイマーやネットワーク、ディレイ呼び出しでも利用されています。
 なのでNSTimerをサブスレッドで実行しても、RunLoopが実行されていないのでタイマー呼び出しは機能しません。
 例えば同じくRLAppDelegate.mで
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  ・・・
// 1秒ごとに"run on main..."メッセージをコンソールに出力
[NSTimer scheduledTimerWithTimeInterval:1 target:self
selector:@selector(oneJob:) userInfo:@"main" repeats:YES];

// サブスレッド作成
[self performSelectorInBackground:@selector(anotherThreadTask) withObject:nil];


return YES;
}

- (void)oneJob:(NSTimer*)timer
{
printf("run on %s %p main=%p\n",
[timer.userInfo UTF8String],
[NSThread currentThread],
[NSThread mainThread]);
}

- (void)anotherThreadTask
{
// 1秒ごとに"run on sub..."メッセージをコンソールに出力
[NSTimer scheduledTimerWithTimeInterval:1 target:self
selector:@selector(oneJob:) userInfo:@"sub" repeats:YES];
}

とか実装しても、コンソールにはメインスレッド側のタイマー処理ばっか出るんですな。

$テン*シー*シー-9
メインスレッド側タイマー処理だけ実行されてる

 どうしてもサブスレッドでタイマー使いたい場合は-anotherThreadTaskメソッドで
- (void)anotherThreadTask
{
// 1秒ごとに"run on sub..."メッセージをコンソールに出力
[NSTimer scheduledTimerWithTimeInterval:1 target:self
selector:@selector(oneJob:) userInfo:@"sub" repeats:YES];
[[NSRunLoop currentRunLoop] run];
}

 てのを追加します。

------------
サンプルプロジェクト:RunLoop-2.zip

 NSRunLoopの+currentRunLoopで、動作中のスレッド用のNSRunLoopオブジェクトを取り出し(なければ作られる)て、そいつに-runメッセージを送るわけです。
 直接+alloc、-initでの作成は駄目みたい。あと、実行中のスレッド用以外のNSRunLoopオブジェクトを使うのも御法度みたいっす。
 ユーザーのタップなんかはメインスレッド用だけ検出させ、サブスレッド用では検出しないように種類分けしてるんじゃないかと思われ。
 とにかくこれで、1秒間隔でメイン、サブスレッドともコンソールにメッセージが出力される。

$テン*シー*シー-10
出た

 NSURLConnectionもサブスレッドで+connectionWithRequest:delegate:なんかで実行しても、ぜんぜんNSURLConnectionのDelegateメソッドが呼び出されません。これもNSRunLoopの-runが必要なんですな。
 昔、別スレッドでNSURLConnectionを呼び出した時に、思いっきりハマりますた。
Delegateメソッドを+connectionWithRequest:delegate:を実行したスレッド上で実行するようにさせるために、このRunLoopが必要だったんですな。

------------
サンプルプロジェクト:RunLoop-3.zip

 -performSelector:withObject:afterDelay:なんかもNSRunLoopが必要。

------------
サンプルプロジェクト:RunLoop-4.zip

 あと、NSRunLoopの-runは動きっぱなしになるんで、以下のような呼び方が柔軟な対応ができてモアベターってNSRunLoopの-runメソッドの説明で書かれてます。
BOOL shouldKeepRunning = YES; // global
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning
&& [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
NSRunLoop Referenceより抜粋

 というところで、今日はこれまで。

 次回は今回のサンプルでも頻繁に登場する
NSThread

を、もうちょっと(-performSelectorInBackground:withObject:は中でNSThreadを作って利用して同時並行処理を実現する)と、別々のスレッドが同じ変数に順序よくアクセスする方法について。