2012年1月11日 星期三

iOS - Thread - NSOperation


目的
  • 使用NSOperationQueue達成執行緒實作
工具
  • NSOperation
    • 執行緒
  • NSOperationQueue
    • 執行緒池,將NSOperation放入交由系統執行
  • NSCondition
    • 類似JAVA中的synchronized機制,使用"lock" & "unlock"防止多執行緒同時存取相同的變數
使用範例
﹣實現Producer & Consumer

Store.h
// Store用來管理Producer及Consumer一同存取的容器 
// 故需要考慮到同步的問題,不可發生同時存取相同容器的問題 

@interface Store : NSObject 
{ 
  NSMutableArray* mProducts; NSCondition* mLocker; 
} 

- (id) initStore; 
- (void) add:(NSNumber*) product; 
- (NSNumber*) get; 

@end

Store.m
#import "Store.h"  

@implementation Store 

- (id) initStore
{
self = [super init];
if (self){
mProducts = [[NSMutableArray alloc] init];
mLocker = [[NSCondition alloc] init];
}

return self;
}

- (void) add:(NSNumber*) product
{
// 同步add product階段
[mLocker lock];
while ([mProducts count] >= 2)
{
[mLocker wait];
}
[mProducts addObject:product];
[mLocker signal];
// wake up one thread
[mLocker unlock];
}

- (NSNumber*) get
{  
// 同步get product階段
[mLocker lock];
while ([mProducts count] <= 0)  
{
[mLocker wait];
NSNumber* product = [mProducts objectAtIndex:0];
[mProducts removeObjectAtIndex:0];
[mLocker signal];
// wake up one thread
[mLocker unlock]; return product; 
}
@end

Producer.h
// 將數字放入容器中  #import "Store.h" 
@interface Producer : NSOperation
{
Store* mStore;
}

- (id) initProducer:(Store*) store;
- (void) main;

@end
Producer.m
#import "Producer.h" 
@implementation Producer
- (id) initProducer:(Store*) store
{
self = [super init];
if (self) 
{ mStore = store; } return self; } - (void) main { for (int iProduct = 1; iProduct <= 10; iProduct++) { // 隨機睡眠 0~4秒 [NSThread sleepForTimeInterval:(arc4random() % 5)]; [mStore add:[NSNumber numberWithInt:iProduct]]; NSLog(@"Produce %d", iProduct); } } @end
Consumer.h
// 將收字從容器中拿出 #import "Store.h" 
@interface Consumer : NSOperation
{
Store* mSotre;
}

- (id) initConsumer:(Store*) store;
- (void) main;

@end

Consumer.m
#import "Consumer.h"

@implementation Consumer

- (id) initConsumer:(Store*) store
{
self = [super init];
if (self)
{
mSotre = store;
}

return self;
}

- (void) main
{
for (int iLoop = 0; iLoop <= 10; iLoop++)
{
[NSThread sleepForTimeInterval:(arc4random() % 5)];

NSLog(@"Consume %d", [[mSotre get] intValue]);
}
}
@end
ViewController.h
#import "Producer.h"
#import "Consumer.h"
#import "Store.h"

@interface ViewController : UIViewController
{
NSOperationQueue* mQueue;
Producer* mProducer;
Consumer* mConsumer;
Store* mStore;
}

@end
ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];

mQueue = [[NSOperationQueue alloc] init];
mStore = [[Store alloc] initStore];

// 將Store放入Producer & Consumer裡
mProducer = [[Producer alloc] initProducer:mStore];
mConsumer = [[Consumer alloc] initConsumer:mStore];

// 將Producer & Consumer放入Operation queue中,
// 開始執行Producer & Consumer
[mQueue addOperation:mProducer];
[mQueue addOperation:mConsumer];
}
Note
  • 其中"[mLocker signal];" 可代換成"[mLocker broadcast];",差別在於前者"signal"喚醒一個等待此thread的thread;而後者喚醒所有在等待此thread的thread

2012年1月10日 星期二

iOS - ASIHTTPRequest

目的
  • 用於網路溝通
工具
使用範例
﹣欲下載網路圖片如下
圖片來源
﹣Code
.h
#import <UIKit/UIKit.h>
#import "ASIHTTPRequest.h"
#import "ASINetworkQueue.h"

@interface ViewController : UIViewController <ASIHTTPRequestDelegate>
{
    NSMutableData* mDataBuf;
    ASINetworkQueue* mQue;
    NSArray* mUrls
}

@end

.m
- (void)viewDidLoad
{
  [super viewDidLoad];  
  // 下載的緩衝區
  mDataBuf = [[NSMutableData alloc] init];
  
  // 執行Request區域,類似NSOperationQueue,
  // 故在執行Request時是以執行緒方式進行,不影響原執行緒  
  mQue = [[ASINetworkQueue alloc] init];
  [mQue reset];
  [mQue setShowAccurateProgress:YES];
  [mQue go];


  // 設定下載位址

  ASIHTTPRequest* request = [[ASIHTTPRequest alloc] initWithURL:url];

  // 設ASIHTTPRequest delegate,用於回傳訊息並處理
  request.delegate = self;

  // 開始處理Request
  [que addOperation:request];
}

// ASIHTTPRequestDelegate 接收到資料後

- (void)request:(ASIHTTPRequest *)request didReceiveData:(NSData *)data
{
    [mDataBuf appendData:data];
    
    NSLog(@"Recevie %d bytes...", [data length]);
}

// ASIHTTPRequestDelegate 資料接收完成
- (void)requestFinished:(ASIHTTPRequest *)request 
{
  // 取得存放位置
  NSArray *pathList = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  NSString *path = [pathList objectAtIndex:0];    
  
  // 指定
資料夾
  path = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"/tmp/test"]];
    
  // 若指定資料夾不存在則創建
  NSFileManager* fileM = [NSFileManager defaultManager];
  NSError* error;
  if(![fileM fileExistsAtPath:path]) [fileM createDirectoryAtPath:path withIntermediateDirectories:true attributes:nil error:&error];

  //加上檔名
  path = [path stringByAppendingPathComponent: [NSString stringWithFormat: @"/00.jpg"]];
  NSLog(@"儲存路徑:%@", path);

  //寫入檔案
  [mDataBuf writeToFile:path atomically:NO];
  mDataBuf = [[NSMutableData alloc] init];
    
  NSLog(@"Download success");
}

Note
  • ASIHTTPRequestDelegate還有許多回傳方法,依需要來實作
  • 加入專案時可能會發生一堆類似的參考錯誤 "Apple Mach-O Linker (id) Error"
上述錯誤需加入sqlite3的library
進入TARGETS

依紅框指示加入參數和library,以解除其它參考的錯誤

2012年1月2日 星期一

iOS - Regular Expression

目的
  • 使NSString型態可以使用Regular Expression來表示

工具
  • RegexKitLite: 開源碼,輕量化的regular expression物件。
  • Download (官網
  • 解壓縮後將RegexKitLite.h 和 .m複製到要使用的專案中
  • 再加入"-fno-objc-arc"參數,避免ARC機制的啟動

範例
NSError* error;

// 取得Yahoo的HTML內容

NSString* tmp = [NSString stringWithContentsOfURL:[NSURL URLWithString:@"http://www.yahoo.com.tw"] encoding:NSUTF8StringEncoding error:&error];

// 使用regular expression過濾內容
    NSString* regEx = @"<title>(.*)</title>";
    for(NSString* sub in [tmp componentsMatchedByRegex:regEx])
    {
        NSLog(sub);
    }

結果

2012年1月1日 星期日

iOS - 爬 HTML

目標
  • 抓取網頁HTML
  • 分析HTML
工具
  • libxml2: iOS原有的library,parse xml
  • 點選紅框標示的地方
    點選紅框標示的地方,最後使用“+”把libxml2加入


    在紅框的地方搜尋“other linker”,再加入“-lxml2”參數

  • Hpple: 開源碼,幫助分析HTML
    • Download
    • 將資料夾中的TFHpple, TFHppleElement, XPathQuery複製到要開發的資料夾中
    • 再加入“-fno-objc-arc”參數關閉ARC功能

範例
- (void)viewDidLoad
{
    [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
    
    NSError* error;

    // 取得yahoo首頁的HTML
    NSData* data = [[NSString stringWithContentsOfURL:[NSURL URLWithString:@"http://www.yahoo.com.tw"] encoding:NSUTF8StringEncoding error:&error] dataUsingEncoding:NSUTF8StringEncoding];

    // 取出title標籤中的值
    TFHpple* parser = [[TFHpple alloc] initWithHTMLData:data];   
    NSArray* values  = [parser searchWithXPathQuery:@"//title"]; // get the title
    TFHppleElement* value = [values objectAtIndex:0];
    NSString* result = [value content];

    NSLog(@"result = %@", result);
    mLabel.text = result
}

問題
  • Compile後會發生"<libxml/tree.h> not found"
    • 解:在Header search path中加入“${SDKROOT}/usr/include/libxml2”參數(${SDKROOT}會自動變為root路徑)
先在搜尋欄中搜尋“header search",再將參數輸入


結果

Qbo 機械人懂得分辨自己了

Qbo 機械人懂得分辨自己了