2010年9月30日木曜日

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

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

libxml を使ってフィードを取り込む準備にかかろう。


パースするためのコンテクストとしてインスタンス変数を用意する。
  1. @interface RSSImporter : NSObject {  
  2. // ここ追加  
  3. @private  
  4.  xmlParserCtxtPtr context;  
  5. }  
  6.   
  7. - (void)fromC; // ここ追加(でも、あとで消す)  
  8.   
  9. @end  


パーサ用のコンテクストを作る。←よくわかってない
サーバからデータを受信するごとにパーサに処理させたいのでxmlCreatePushParserCtxt()関数を使う。
xmlCreatePushParserCtxt()の第2引数で任意のデータとしてselfを指定しているのに注目。
通知されるC関数に対して、この任意のデータが引数で渡される。

  1. // RSSImporter.m の該当箇所を編集すること  
  2.   
  3. //  
  4. // ここから普通に@implementationです  
  5. //  
  6. @implementation RSSImporter  
  7.   
  8. /** 
  9.  */  
  10. - (id)init {  
  11.    
  12.  self = [super init];  
  13.  if (self != nil) {  
  14.   
  15.   // ここを追加  
  16.   // パーサ用のコンテクストを作る  
  17.   if (!context) {  
  18.    context = xmlCreatePushParserCtxt(&simpleSAXHandlerStruct, self, NULL, 0, NULL);  
  19.   }  
  20.     
  21.     
  22.   NSString *feedURL = @"http://eyesrobe.blogspot.com/feeds/posts/default?alt=rss";  
  23.   NSURLRequest *requestURL = [NSURLRequest requestWithURL:[NSURL URLWithString:feedURL]];  
  24.   NSURLConnection *urlConnection = [[[NSURLConnection alloc] initWithRequest:requestURL delegate:self] autorelease];  
  25.   if (urlConnection == nil) {  
  26.    NSLog(@"error");  
  27.   }  
  28.  }  
  29.    
  30.  return self;  
  31. }  
  32.   
  33.   
  34. /** 
  35.  ちょっとした確認に使う。いらなくなったら消す。 
  36.  */  
  37. - (void)fromC {  
  38.  NSLog(@"C言語の関数からMethod呼び出せるって不思議");  
  39. }  
  40.   
  41. - (void)dealloc {  
  42.  [super dealloc];  
  43. }  
  44.   
  45. #pragma mark NSURLConnection Delegate methods  
  46. //  
  47. // ここから下のメソッドが、NSURLConnectionのデリゲートメソッド  
  48. //  
  49.   
  50. - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {  
  51.  // データ受信  
  52.  NSLog(@"受信中(データは分割されて受信される)");  
  53.   
  54.  // これ追加  
  55.  // 受信したデータをパーサに渡している  
  56.  // NSDataはC言語では扱えないのでchar型にキャストして渡してる   
  57.  xmlParseChunk(context, (const char *)[data bytes], [data length], 0);  
  58. }  
  59.   
  60. - (void)connectionDidFinishLoading:(NSURLConnection *)connection {  
  61.  // 受信完了  
  62.  NSLog(@"受信完了");  
  63.   
  64.  // ここを追加  
  65.  // パーサを解放  
  66.  if (context) {  
  67.   xmlFreeParserCtxt(context);  
  68.   context = NULL;  
  69.  }  
  70. }  
  71.   
  72. - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {  
  73.  // 受信エラー  
  74.  NSLog(@"エラー");  
  75.   
  76.  // ここを追加  
  77.  // パーサを解放  
  78.  if (context) {  
  79.   xmlFreeParserCtxt(context);  
  80.   context = NULL;  
  81.  }  
  82. }  
  83.   
  84. @end  



xmlCreatePushParserCtxt()の第2引数で任意のデータとして指定しているselfは、
startElementSAX()やendElementSAX()やcharactersFoundSAX()などのC関数の第1引数で void *context として渡されてる。
イメージとしては、 context == self って感じ。
selfで渡されたデータを処理しやすいように、C関数の中でオブジェクトとして扱える形にすると、C関数の中でObjective-Cなメソッドを呼び出せるようになったり。
こんな感じで。RSSImporter *parserImporter = (RSSImporter *)context;

  1. // RSSImporter.m の該当箇所を編集すること  
  2.   
  3. static void startElementSAX(  
  4.        void *context,  
  5.        const xmlChar *localname,  
  6.        const xmlChar *prefix,  
  7.        const xmlChar *URI,  
  8.        int nb_namespaces,  
  9.        const xmlChar **namespaces,  
  10.        int nb_attributes, int nb_defaulted,  
  11.        const xmlChar **atrributes) {  
  12.  // 要素開始の通知を受けたときの処理  
  13.  // ここ追加  
  14.  // void *context は Objective-C との橋渡し。  
  15.  // xmlCreatePushParserCtxt()の第2引数でselfとして渡されたのが、contextとして渡ってきた。  
  16.  // 型を合わせることで、Objective-Cのオブジェクトとして扱えるようにしている。  
  17.  RSSImporter *paserImporter = (RSSImporter *)context;  
  18.  [paserImporter fromC];  
  19. }  



ビルドして実行。

続く。

0 件のコメント:

コメントを投稿