同時進行処理のNSThreadを試すにあたって、なんかビジュアル的な要素が欲しいなあと思ってるんですよ。
 printfにもあきたしな。

 ということでunsigned char型の1次元配列を用意して、こいつを同時並行で加工して、それを画像として画面に表示します。
 iOSで基本的なカラー画像は、1ピクセルが4バイトで構成されてて、青、緑、赤、α順にそれぞれの照度値が1バイトで並んどります。
 なので100 x 100の大きさの画像なら
unsigned char* _base = malloc(4 * 100 * 100);
で画像に必要な記憶エリアが確保できるわけです。

$テン*シー*シー-1

 この記憶エリアをunsigned charの1次元配列と考えて、直接同時並行で加工してみようとわけですよ。
 同時におこなうのは上からの塗りつぶしと、任意の位置への矩形表示。

$テン*シー*シー-2

 これくらいなら処理も簡単で、sizeが画像の大きさ、_baseが配列の先頭とすると、塗りつぶしなら

for (int v = 0; v < size.height; v++) {
for (int h = 0; h < size.width; h++) {
unsigned char* p = base + (int)((v * size.width * 4) + (h * 4));
*p++ = 0;
*p++ = 0;
*p++ = 0;
*p++ = 255;
}
}
ランダム矩形表示ならblockSizeが任意の位置へ表示する矩形の大きさとして

int x = rand() % (int)(size.width - blockSize.width);
int y = rand() % (int)(size.height - blockSize.height);
for (int v = y; v < y + blockSize.height; v++) {
for (int h = x; h < x + blockSize.width; h++) {
unsigned char* p = base + (int)((v * size.width * 4) + (h * 4));
*p++ = 255;  青色表示ね。
*p++ = 0;
*p++ = 0;
*p++ = 255;
}
}
で記述できちゃうわけですよ。

 こいつらをNSThreadやpthread、GCDで同時平行処理で動かしていろいろ試してみようちゅーわけです。

 で、このunsigned charの1次元配列を画像として表示するにはCGBitmapContextCreateを使います。第1引数に1次元配列の先頭番地_baseを与えて、残りの引数で、どんな画像にするか(グレーにするか、カラーにするかとか)の情報を設定してやれば、画像加工用のCGContextRefを作ってくれます。

- (void)createBaseWithSize:(CGSize)size
{
CGSzie _size = size;
unsigned char* _base = malloc(4 * _size.width * _size.height);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef _bmContext = CGBitmapContextCreate (
_base,     画像バッファ
_size.width,   
_size.height,   高さ
8,        R、G、B、αの1つの要素が使うビット数
4 * _size.width, 横1行分のバイト数
colorSpace,   カラーにする
(kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst)); RGBαの並び
CGColorSpaceRelease(colorSpace);
}
あとは、CGBitmapContextCreate関数で返されたCGContextRefを使って
CGImageRef image = CGBitmapContextCreateImage(_bmContext);
とかすれば画像として利用できマッスル。

 そんなわけで、まずはFile→New→Projectメニューで新規プロジェクト作成。
 今回は同時並行処理で、GUIにどう影響するかも見たいので、テンプレートにUtility Application選択。

$テン*シー*シー-3

 設定はこんな感じで、Universal、ARC Onでいくことにします。

$テン*シー*シー-4

 で、加工するのはMainViewController.m。そこに今回のunsigned char型の1次元配列を管理して画面に表示するカスタムUIViewクラスを定義しています。
@interface GView : UIView

@property (readonly) unsigned char* base; // 配列の先頭
@property (readonly) CGSize size; // 画像の大きさ
@end

@implementation GView {
CGContextRef _bmContext; // 作業用コンテキスト
}
@synthesize base = _base;
@synthesize size = _size;

// 作業用コンテキスト、配列の破棄
- (void)dealloc
{
CGContextRelease(_bmContext);
free(_base);
}

// 指定された大きさの画像用のコンテキストと、そのバッファとなるunsigned char配列の確保
- (void)createBaseWithSize:(CGSize)size
{
if (_base) { // 古いものは破棄
CGContextRelease(_bmContext);
free(_base);
}
_size = size;
_base = malloc(4 * _size.width * _size.height);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
_bmContext = CGBitmapContextCreate (_base,
_size.width,
_size.height, 8,
4 * _size.width,
colorSpace,
(kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst));
CGColorSpaceRelease(colorSpace);
}
// 画像を表示
- (void)set
{
CGImageRef image = CGBitmapContextCreateImage(_bmContext); // 画像生成
[CATransaction begin];
[CATransaction setDisableActions:YES]; // アニメーションはいらない
self.layer.contents = (__bridge id)image; // 画像をレイヤー内容部に指定
[CATransaction commit];
CGImageRelease(image);
}

とこんな感じです。
 -drawRect:メソッドをオーバーライドしてもいいんですが、今回はlayerのcontentsにCGImageRefを貼付けました。
 
 で、これをMainViewControllerのビューに貼付けて利用するわけですよ。
 MainViewControllerのビューが生成された直後に呼ばれる-viewDidLoadメソッドで
- (void)viewDidLoad
{
[super viewDidLoad];
_view = [[GView alloc] initWithFrame:self.view.bounds];
_view.autoresizingMask = UIViewAutoresizingFlexibleWidth
|UIViewAutoresizingFlexibleHeight;
[_view createBaseWithSize:CGSizeMake(2000, 2000)];
[self.view insertSubview:_view atIndex:0];
}
て感じで貼付ける。
 -addSubview:を使わず-insertSubview:を使うわけは、Utility Applicationテンプレートでは裏側のビューに戸板返しさせるボタンが、すでに配置済みなんで、その上にGViewを置きたくなかったから。

 あとは、0.1秒ごとにGViewのunsigned char配列を画像として表示する処理をNSTimerで実行っす。
- (void)viewDidLoad
{
  ・・・
[self.view insertSubview:_view atIndex:0];

_timer = [NSTimer scheduledTimerWithTimeInterval:0.1
    target:self selector:@selector(update) userInfo:nil repeats:YES];
}

- (void)update
{
[_view set];
}
 これがメインスレッドの動き。

 ここに、同時並行処理として、さっきの上から塗りつぶす、ランダムに四角を表示するを別々に同時実行させるとどうなるか。
- (void)viewDidLoad
{
  ・・・
_timer = [NSTimer scheduledTimerWithTimeInterval:0.1
    target:self selector:@selector(update) userInfo:nil repeats:YES];

[self performSelectorInBackground:@selector(draw) withObject:nil];
[self performSelectorInBackground:@selector(wipeout) withObject:nil];
}
 -performSelectorInBackground:withObject:で指定してるメソッドで、最初のunsigned char型の1次元配列の加工をやるわけです。-performSelectorInBackground:withObject:が謎な人はその(232)を参照してね。
//ランダムに四角を表示する
- (void)draw
{
for (;;) {
ここでランダムに四角を表示する処理
[NSThread sleepForTimeInterval:0.1];
}
}
// 上から塗りつぶす
- (void)wipeout
{
unsigned char* base = _view.base;
CGSize size = _view.size;
for (;;) {
for (int v = 0; v < size.height; v++) {
ここで 1ライン塗りつぶしの処理
[NSThread sleepForTimeInterval:0.001];
}
}
}
 ループは完全に無限ループにしとります。
 無限ループ同士、出会ってはならない二人が出会ってしまった。
 なのにちゃんと両方動く。
 同時並行だから、ビバ同時並行。
 ギャラクシーはクアドコアだし、iOSもCoreImage filter解禁(まだプログラマブルまでは解禁してないけど)したし、時代は同時並行ですな。

 NSThreadの+sleepForTimeInterval:で適度に時間を置きながら実行させてるんで、雨が降ってきて、ワイパーで拭き取ってる感じの画面なります。

$テン*シー*シー-5

 ランダムで表示している矩形が描き終わる前に一度消されて、途中からの描写になったりと、なかなか見た目に同時並行処理が楽しめます。
 ほんとに波紋にするってのも、ちょっとめんどくさそうだけど、面白いかもね。

 しかし、ピクセルごとに位置計算しても、サクサク動くってZ80時代には考えられね~。
------------
サンプルプロジェクト:thread.zip