2010年11月2日火曜日

Objective-C + libxml2 を使ってフィードを取り込みたいんだ(9)

なんかもうlibxml2のことは関係なく普通にObjective-Cの勉強になってる。

今回は、
  • NSOperationQueue
  • NSOperation
  • NSNotificationCenter
  • NSNotification

に焦点をあわせる。

でもNSNotificationの辺りは
「Cocoa Notification (NSNotification,NSNotificationCenter)」と「キー値監視」(1~4)
に書いてるので省略。
1つだけ注意するのは、通知の受け取りをメインスレッドで行ないたい場合は、通知する側でperformSelectorOnMainThread:withObject:を使って通知を投げる必要あり。


NSOperationQueueとNSOperationについてもあまり書く事はないんだけど続ける。
覚えたこと。
  • 並列と非並列とがある。
  • キューにNSOperationのインスタンスを貯めて置いて、順番に処理を起動していく。
  • 並列は、1つの処理の完了を待たずに次の処理を起動する。(複数の別スレッドで処理される)
  • 非並列は、1つの処理が完全に終了するまで次の処理を起動しない。(全てメインスレッドで処理される)
  • iOS4からは[[NSOperationQueue alloc] init]で初期化した場合は、並列になる。NSOperationなインスタンスのisConcurrentの値は関係なく。
  • iOS4で非並列に処理させたい場合は、[NSOperation mainQueue] ってやる。


注意すること。
並列で処理実行するときは、NSRunLoopの実行ループをループさせておいてのイベント処理が必要な場合がある(間違ってるかも)。
今回は必要なのでRunLoop入れてあります。


あーもう、ぐちゃぐちゃだーーー!!!
あとはソース見てください。


次回の内容は未定。



Xcode 3.2.4 and iOS SDK 4.1 で試してる。
古いバージョンとか新しいバージョンのこと考えてない。

新規プロジェクトをView-based Applicationを選択して名前をMyOperationとして作成してます。
新規ファイル追加で、NSOperationを継承したMyClassを作成しています。


//
// MyOperationViewController.h
//
#import

@interface MyOperationViewController : UIViewController {
}
@end


//
// MyOperationViewController.m
//
#import "MyOperationViewController.h"
#import "MyClass.h"

@implementation MyOperationViewController

- (void)owatayo:(MyClass *)obj
{
//NSLog(@"%@", obj);
//NSLog(@"no.%d", [[(MyClass *)[obj object] number] intValue]);
NSNumber *num = [[obj userInfo] objectForKey:@"number"];
NSLog(@"[no.%d] メインスレッド:通知owataを受け取ってowatayoメソッドを実行したよ。", [num intValue]);
}

- (void)viewDidLoad
{
[super viewDidLoad];

// 読み込むフィード
NSString *feedURL1 = @"http://eyesrobe.blogspot.com/feeds/posts/default?alt=rss";
NSString *feedURL2 = @"http://blog.eyesrobe.com/feed/rss";
NSArray *feeds = [NSArray arrayWithObjects:feedURL1, feedURL2, feedURL1, feedURL2, feedURL1, feedURL2, feedURL1, feedURL2, nil];

// 通知
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];

// キューの初期化
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

// NSOperationQueue に許す同時接続スレッドの数
// スレッド数を制限した場合としない場合の実行内容を比較してみるといいかも。
[queue setMaxConcurrentOperationCount:3];

// キューにNSOperationなオブジェクトを追加する。
// 追加されたオブジェクは別スレッドで順に処理されていく。
int i=0;
for (NSString *feedURL in feeds) {

NSURLRequest *requestURL = [NSURLRequest requestWithURL:[NSURL URLWithString:feedURL]];
MyClass *obj = [[MyClass alloc] initWithRequest:requestURL];
obj.number = [NSNumber numberWithInt:(++i)];
[center addObserver:self selector:@selector(owatayo:) name:@"owata" object:obj];
[queue addOperation:obj];
[obj release];
}

// キューが全て処理されるまで待つメソッド
// [queue waitUntilAllOperationsAreFinished];
//
// 待たないで処理をバックグラウンドで処理するのであれば、NSNotificationCenterなど
// を使ってバックグラウンド処理完了の通知を受け取ることが多いみたい。

[queue release];
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}

- (void)viewDidUnload {
}

- (void)dealloc {
[super dealloc];
}

@end


//
// MyClass.h
//
#import

@interface MyClass : NSOperation {
NSNumber *number;
NSURLRequest *requestURL;

BOOL done;
}

@property (nonatomic, retain) NSNumber *number;
@property (nonatomic, retain) NSURLRequest *requestURL;

- (id)initWithRequest:(NSURLRequest *)requestURL_;

@end


//
// MyClass.m
//
#import "MyClass.h"

@implementation MyClass

@synthesize number;
@synthesize requestURL;

/**
* 初期化
*/
- (id)initWithRequest:(NSURLRequest *)requestURL_
{
self = [super init];
if (self != nil) {
self.requestURL = requestURL_;
}

return self;
}

/**
* キューに追加されることで実行されるメソッド
*/
- (void)main
{
NSLog(@"[No.%d] MyClass::main", [self.number intValue]);

// NSURLConnection
NSURLConnection *urlConnection = [[[NSURLConnection alloc] initWithRequest:self.requestURL delegate:self] autorelease];

if (urlConnection == nil) {
NSLog(@"error");
}
else {
// データ受信完了フラグ
done = NO;

// NSOperationQueueで別のスレッド動かす場合、NSAutoreleasePoolは要らないみたいだけど、NSRunLoop(実行ループ?)は必要っぽい。
// 処理実行中(done==NO)の間は、実行ループをループさせ続けることが必要。
// ここでは、NSURLConnectionによるデータ受信完了時点で処理完了(done=YES)としている。
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
} while (!done);
}

// 通知するときに一緒に送りたいメッセージ
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:number, @"number", @"value", @"key", nil];

// 処理が終わったら通知メッセージを送る。
// メインスレッドで実行させたいので performSelectorOnMainThread:withObject:waitUntilDone: を使った。
NSNotification *notification = [NSNotification notificationWithName:@"owata" object:self userInfo:dict];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center performSelectorOnMainThread:@selector(postNotification:) withObject:notification waitUntilDone:NO];
}

/**
* データ受信
*/
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(@"[No.%d] 受信中(データは分割されて受信される)", [self.number intValue]);
}

/**
* 受信完了
*/
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(@"[No.%d] 受信完了", [self.number intValue]);
done = YES;
}

/**
* 受信エラー
*/
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(@"エラー");
}

/**
* dealloc
*/
- (void)dealloc {
[number release];
[requestURL release];
[super dealloc];
}

@end


たぶん続く。

0 件のコメント:

コメントを投稿