ASIHTTPRequest 筆記

ASIHTTPRequest 是 Objective-C 上知名的專門用來處理 HTTP requests 的框架,不過,作者似乎已經停止更新了(但是在 iOS 5 還是運作得很正常)。前陣子發現另一個 network framework 叫 AFNetworking,似乎挺不錯的,有空再去試試,雖然我最近寫 mobile app 已經寫到有點煩了…… AFNetworking 是 Gowalla 團隊開發的,對,就是那個被 Facebook 收購然後淒慘地關站的 Gowalla。

Add To Your Xcode Project

詳見:Using ASIHTTPRequest in an iOS project。

直接在 Finder 中把 ASIHTTPRequest 的檔案包含目錄拉進 xcode 裡,然後選擇 “Create groups for any added folders”,目錄的結構會像這樣:

ASINetworkQueue

我通常會在每個 view controller 裡面放一個 ASINetworkQueue 物件,由它來統一處理這個頁面的所有 requests。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
- (void)viewDidLoad {  [super viewDidLoad];   requestQueue = [[ASINetworkQueue alloc] init];   // count = 1,就算是非同步,也會是序列執行  requestQueue.maxConcurrentOperationCount = 15;   // ASIHTTPRequest 默認的情況下,queue 中只要有一個 request fail 了,整個 queue 裡的所有 requests 也都會被 cancel 掉  [requestQueue setShouldCancelAllRequestsOnFailure:NO];   // go 只需要執行一次  [requestQueue go]; }  - (void)viewWillDisappear:(BOOL)animated {  [super viewWillDisappear:animated];   // 離開這個頁面就暫停所有的 requests,之後又回到這個頁面時,可以繼續未完成的請求  [self.requestQueue setSuspended:YES]; }  - (void)viewWillAppear:(BOOL)animated {  [super viewWillAppear:animated];   // 恢復執行 requests  [self.requestQueue setSuspended:NO]; } 

Asynchronous Request(非同步請求)

基本上你應該不會想在 mobile 上使用同步請求,因為它會整個拖住畫面的渲染,也不能響應用戶的操作,所以通常的用法是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
- (void)followMember {  NSURL *url = [NSURL URLWithString:@"http://example.com/member/follow"];   /*  帶 POST / GET query 參數的請求要用 ASIFormDataRequest  下載圖片之類的單純的請求就用 ASIHTTPRequest  */  ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];   /*  通常會把 delegate 指定為當前的 view controller  你把 callback selector 寫在哪裡,delegate 就是哪個  */  [request setDelegate:self];  [request setDidFinishSelector:@selector(didFollowMember:)];  [request setDidFailSelector:@selector(didServerFail:)];   // POST Query  [request setPostValue:@"1234" forKey:@"user_id"];  [request setPostValue:@"5678" forKey:@"target_id"];  [request setPostValue:@"member" forKey:@"follow_type"];   // 放進 queue 的 request 就是非同步,不需要再特地 [request startAsynchronous] 了  [requestQueue addOperation:request]; }  - (void)didFollowMember:(ASIHTTPRequest *)request {  NSLog("request 得到 HTTP 200 的 response 之後,就會執行這個 callback"); }  - (void)didServerFail:(ASIHTTPRequest *)request {  NSLog("server 掛掉、request timeout 或 request 被強制 cancel 掉之後,就會執行這個 callback"); } 

callback selector 一定會在 main thread 執行,在 iOS 中,如果要進行 UI 操作(例如 [self.tableView reloadData] 之類的),必須是在 main thread。

延伸閱讀:

  • ASIHTTPRequest example code
  • ASIHTTPRequest 中文文档

Download Image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
- (void)downloadImage:(NSMutableDictionary *)photoInfo {  NSURL *photoURL = [NSURL URLWithString:@"http://example.com/images/1234.jpg"];  ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:photoURL];   [request setDelegate:self];   // cache  [request setDownloadCache:[ASIDownloadCache sharedCache]];  [request setCachePolicy:ASIOnlyLoadIfNotCachedCachePolicy];  [request setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];  // [request setSecondsToCache:60 * 60 * 24 * 30]; // cache 30 天   // time out 自動重試的次數  [request setNumberOfTimesToRetryOnTimeout:2];   // callbacks  [request setDidFinishSelector:@selector(didDownloadImage:)];  [request setDidFailSelector:@selector(didServerFail:)];  [request setDidReceiveResponseHeadersSelector:@selector(dummySelector)];  [request setDidStartSelector:@selector(dummySelector:)];   /*  userInfo 的資料會被帶去 callback methods  而且 request.userInfo 會 retain 它    在這個例子中  一個頁面可能有很多個 UIImageView  你可以把對應的 UIImageView 放進 userInfo  這樣 callback 才知道要把下載下來的圖片放到哪一個 UIImageView  */  request.userInfo = photoInfo;   [self.requestQueue addOperation:request]; }  - (void)didDownloadImage:(ASIHTTPRequest *)request {  NSData *imageData = [request responseData];  UIImage *image = [[UIImage alloc] initWithData:imageData];   UIImageView *imageView = [request.userInfo objectForKey:@"imageView"];   if (imageView.subviews.count > 0) {  [imageView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];  }   [imageView setImage:image];  [image release]; }  /*  我發現如果沒有指定 setDidReceiveResponseHeadersSelector 和 setDidStartSelector  常常就會發生不明的 crash  所以我就不求甚解地加了這一行    但是老實說我完全不知道為什麼  不過事情都搞到這個地步了  其實我也不是那麼想知道原因  我只想準時下班啊~~~  */ - (void)dummySelector:(ASIHTTPRequest *)request {  // do nothing } 

延伸閱讀:

  • ASIHTTPRequest - 使用 download cache
  • 让 ASIHTTPRequest 不占用主线程 - keakon 的涂鸦馆

Release

在你的 view controller 的 dealloc 這樣寫:

1
2
3
4
5
6
7
8
9
10
11
- (void)dealloc {  for (ASIHTTPRequest *request in requestQueue.operations) {  /*  因為 ASIHTTPRequest 並不會 retain 它的 delegate  這樣可以避免 delegate 被 release 之後  request callback 回來卻找不到東西執行而 crash  */  [request clearDelegatesAndCancel];  } } 

因為 request 被 cancel 之後,會執行 didFailSelector 這個 callback,所以你應該設置 [request setDelegate:nil],這樣就不會執行 callback 了。而 [request clearDelegatesAndCancel] 其實就是 [request setDelegate:nil]; [request cancel];。

原文出自:http://gibuloto.com/blog/asihttprequest/

你可能感兴趣的:(image,xcode,callback,download,Crash,asynchronous)