發表文章

目前顯示的是 一月, 2013的文章

iOS 使用 NSURLConnection 實作續傳的注意事項

了解 http request/response header 的用法, 看實例比較好懂: Sample http range request session - Stack Overflow NSURLConnection 連線 URL 所用的 scheme 為 http/https 時, 可放心在 callback 中將 NSURLResponse 往下轉型為 NSHTTPURLResponse, 這樣才能取得更多 http 特定的資訊 (如 http response header): iphone - When is a NSURLResponse not a NSHTTPURLResponse? - Stack Overflow 依 SDK 6.0 NSURLConnection.h 裡所說的, cancel 之後之後仍有可能呼叫 callback The -cancel message hints to the loader that a resource load should be abandoned but does not guarantee that more delegate messages will not be delivered. If -cancel does cause the load to be abandoned, the delegate will be released without further messages. In general, a caller should be prepared for -cancel to have no effect, and internally ignore any delegate callbacks until the delegate is released. 感覺上除了 fail 或 finished 兩個 callback 之外, 其餘情況無法保證 NSURLConnection 的生命週期已結束。呼叫 cancel 後不能接著呼叫 release/autorelease, 否則可能會造成 crash。這和目前官網 (iOS v5.0) 所說得不同, 使用 cancel 時要留意一下實際情況。有過一些 async IO 開發經驗後, 可以理解為何不方便「保…

iOS 實作 "Open in" 的注意事項

幾個重點 使用 UIDocumentInteractionController, How to let your iOS app open files in OTHER apps. 有範例 若是提供 view 給 UIDocumentInteractionControlle 的話, 其中 rect 可用 CGRectZero 表示選單顯示在左上角 UIDocumentInteractionController 初始化後沒有占有 reference count, 記得 retain 否則之後會 crash 自己的 app 會複製一份 app 到別人的 app 裡, 然後別人的 app 開啟它自己的複製品 UIDocumentInteractionController.UTI 有可能偵測得不對, 看文件說它會自動偵測的樣子, 但用起來卻不是那麼一回事, 下載 foo.pdf, 卻沒有呼叫 iBooks。參考 The iOS 5 Developer's Cookbook: Documents and Data Sharing 填入自己用副檔名或 mime type 偵測的 UTI, 就有正常運作。

整合版本管理系統在 IDE 裡使用 vs. 直接使用版本管理系統

不論是使用命令列或視窗圖形介面的版本管理系統 (svn, hg, git, etc),我都偏好直接使用版本管理系統。這有許多好處: 換開發環境時,不用重新學習一次。熟悉版本管理系統的功能愈多,重新學習的成本愈高。 IDE 內的 plugin 有可能是比較舊的版本 最近寫了一陣子 Android 和 iOS 程式後,對這個選擇更有信心。不論現在是用 vim + C++ 寫伺服器端 、用 vim + Python 寫網站、用 XCode 寫 iOS、用 Eclipse 寫 Android,通通都是一樣的方式使用版本管理系統,省了不少力氣適應不同的開發環境。 現今的 IDE 或編輯器都會自動偵測檔案更動時間,在別的地方用版本管理系統更動檔案,切回 IDE 或編輯器後會自動更新,不會編輯錯內容。讓這個選擇更無風險。

Programming in Objective 5e

上網查了一下 Objective-C 的入門書, 大部份人推薦《Programming in Objective-C》 (目前出到第五版) 還有讀 Apple 官方文件。可惜的是, 本書是針對沒學過程式語言的人, 暫時沒找到如《Python Essential Reference》那樣針對有經驗開發者的書 (見 學 Python 的入門書 )。 每天搭車時大概掃一下, 很快就讀完這本書了, 大部份情況是跳著讀範例程式碼, 有疑惑的話再看附近的描述。 最近讀書和開發的過程裡, 順手作的筆記如下:記憶體管理: Objective-C memory managementCategory: Objective-C category 和 informal protocolNSMutableDictionary 的限制、Category 的限制和 Associative Referencedynamic binding 運作原理: Objective-C 的 id, NSObject* 和 id<NSObject> 和 dynamic binding 的運作方式 多數情況對應已學過的概念到語法上即可。基本上 Objective-C 是 C 的 super set, 底層實作也是轉化為 C 的 struct 和指標, 所以滿容易猜中正確用法。 整體來說是滿有意思的語言, 令人意外的是它比 Python、Java、JavaScript等熱門「新」語言還早出現。考量到應用範圍, 除非要長期專職寫 iOS 的應用程式, 不然不會深入研究這個語言。接下要逐步熟悉 IDE、標準函式庫等項目, 這比熟悉如何寫出道地的 Objective-C 來得務實。2013-01-25 更新今天去天瓏才發覺出中文版了, 偶而還是要逛逛書店才行...

Objective-C 的 id, NSObject* 和 id<NSObject> 和 dynamic binding 的運作方式

相關資料:Unixjunkie Blog: id vs NSObject* vs id<NSObject>NSOBject allocruntime/objc.h 摘要心得: id 類似 void*, 不等於 NSObject*。像是 NSProxy 沒有繼承自 NSObject。 實務上不太會用到 NSObject*, 而會用到 id<NSObject>, 因為我們關心它是否符合 NSObject protocol, 不關心是否繼承自 NSObject。 id 實際上是 struct objc_object 的指標, 這個 struct 只有一個欄位 Class isa。而 [NSObject alloc] 會存型別資訊到 isa 裡, 所以 runtime 可以正確得知型別為 id 的物件是否有提供呼叫的方法。 id<SomeProtocol> 中多提到 SomeProtocol, 是協助編譯時的型別檢查。編譯器有愈多資訊, 愈能在編譯時找到錯誤, IDE 也有機會提供更多協助。

XCode unit test 和 NSURLConnection

XCode 的 unit test學習 test、寫 test、維護 test 和開發時執行 test 的等待時間都有成本。若使用的成本太高, 寫起來就更不划算了。初步使用 XCode 內建的 OCUnit 後, 覺得用起來不太划算。沒有 Python 和 Java 那種流暢的感覺。看了別人的討論, third-party unit test framework 還未出現救世主, 期待 XCode 5.x 會不會有友善的環境。 除單獨執行某組 test 或從上回失敗的 test 開始執行這類基本需求外, Python/Java 各有一大好處: Python + nosetests: 可以自行搜集目錄下的 unit test Java + Eclipse: 可以 100% 直線式進行 TDD: 寫 test 呼叫目標 class 和 method -> 用 Eclipse 產生空的目標 class 和對應的 method -> 實作 method。這種寫法頗痛快的而 XCode + OCUnit 令人意外的只能執行全部 test。介面的部份待用久一點後再來評論。 unit test 與非同步操作unit test 測到如 NSURLConnection 的非同步操作時, 記得在 unit test 裡呼叫 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]] 這樣才有分配時間執行非同步操作, 不要傻傻的等, 想說怎麼 NSURLConnection 的 callback 都沒被呼叫到? 細節見The Run Loop In Cocoa Unit Tests。2013-01-25 更新今天翻閱Test-Driven iOS Development (Developer's Library) 才知道在可以選擇要執行的 test 項目, 這本書薄又滿有料的, 很快就可以翻完。長期開發 iOS 又對 TDD 有興趣的話, 看看應該會有幫助。依我目前作的事來說, 時間先花在熟悉 XCode 和 framework 會比較划算就是了。

NSMutableDictionary 的限制、Category 的限制和 Associative Reference

為了確保 key 的內容不變, NSMutableDictionary 在加入 key/value pair 時, 會複製一份 key。這個決定可以避免許多 bug, 不過遇到不支援 NSCopying 的物件會有些麻煩。 比方說在使用 NSURLConnection 時, NSURLConnection 的 callback 會傳回 connection 做為辦別目前是那個 connection 的 callback。問題在於, 若允許同時下載重覆的 URL 並且需要 NSURLConnection 額外的資訊 (比方說自己暫存用的檔名), 要如何取得額外資訊呢? 一個直覺的作法是用 NSMutableDictionary: 以 NSURLConnection 為 key, value 存放對應的額外資訊。然後就踩到 NSMutableDictionary 要求 NSCopying 的雷。 變通作法是使用 Category 幫 NSURLConnection 加上 "connectionID", 這樣就可以取得自定的辨別值作為 NSMutableDictionary 的 key。不幸的是, Category 不能新增 member field, 所以不能直接用這招。Category 不允許新增 member field 是合理的決定, 因為加了 member field 以後, 要在什麼時候 release 它們? 用 Category 覆寫既有的 dealloc 不是個好主意。 回想一開始的需求, 若還是想在 Category 內加 member field 怎麼辦? 針對此點的變通作法是使用 Associative References。 另一個可行的笨方法是建兩個 NSMutableArray, 一個存 NSURLConnection, 一個存額外資訊。要取用額外資訊時, 先找到 NSURLConnection 的 index, 再用此 index 取出額外資訊。在同時擁有的 connection 數不大的時候 (比方說 <100), 這個作法不會有效能問題。更新Scott提到這個情況使用繼承更簡單, 我完全忘了可以繼承標準函式庫 class, 這個作法比上述作法好。另外提到 C 程式另有使用指標位置位移的技巧取得額外資…

Objective-C category 和 informal protocol

在查 NSURLConnection 的時候, 看到一堆重要的 method 被 deprecated 了, 感到相當困惑。讀了這篇說明才明白, 原來東西都在, 用法其實也沒什麼變, 只是宣告的方式變了。 首先要理解 Objective-C 的語法 Category:@interface SomeClass (SomeCategory) ... @end 在 ... 宣告的方法, 是對 SomeClass 的擴充, 這組擴充的名稱為 SomeCategory。 Category 可用在包含標準函式庫 (Foundation) 內的任何 class。這有什麼好處呢? 比方說你需要一個比較特別的 Set 操作, 像是 [mySet uniformRandomObject] 傳回 mySet 內隨機的一個物件。不論是用組合還是繼承的方式訂新的 class, 都不太方便。這時用 Category 就很方便了:// NSSet+RandomOps.h @interface NSSet (RandomOps) -(id)uniformRandomObject; @end // NSSet+RandomOps.m @implementation NSSet (RandomOps) -(id)uniformRandomObject { ... } @end 再來, Protocol 相當於 Java 的 interface, 也就是只有宣告但不帶實作的介面。另一方面, 只有宣告 Category 卻沒有提供實作的宣告, 就稱為 Informal Protocol。通常會宣告在 NSObject 上, 這樣任何客戶碼都可以實作需要的 method 而不用繼承其它 class。 NSURLConnection deprecated 的那些方法就是採用此方法。在 iOS 4.3 to iOS 5.0 API Differences 搜尋 "NSURLConnection.h" 可看到多數 callback 從 NSObject 裡移到新的 protocol NSURLConnectionDelegateNSURLConnectionDataDelegateNSURLConnectionDownloadDelegate 裡面。 Obj…

Objective-C memory management

主要參考資料是 Programming in Objective-C 5e ch17, 網路上這篇也滿清楚的。 Objective-C 提供三種管理記憶體的方式: automatic garbage collection: 只有 Mac OS 提供, 且在 OS X 10.8 後宣告 deprecated, 所以不該繼續使用。 manual reference counting: 透過一套訂好的 API 和規定函式名稱代表的語意, 讓開發者比較容易管理 reference counting。 automatic reference counting: 透過 compiler 生成 reference counting 相關的程式碼, 開發者不用那麼留意 reference counting, 看起來相當美好, 作者極力讚賞這個機制。Manual Reference Counting (MRC)NSObject 提供 retain 和 release 兩個方法來 +1 / -1 reference count。所有物件都繼承自 NSObject。開發者要記得如下的規則: 擁有物件的人記得要 +1, 不用時記得要 -1 count 為 0 時會呼叫 dealloc, 開發者可自訂 dealloc 釋放自己額外擁有的物件 誰配置這物件, 就是它的初始擁有者 從 copy, mutableCopy, alloc, new 取得的物件, 已經 +1 過了, 不用時記得呼叫 release。有些文章指出有呼叫 init* 開頭的方法表示有 +1, 反之則沒有。像是 [[NSString alloc] initWithUTF8String:X] 之於 [NSString stringWithUTF8String:X] 。這個說法不太正確, 應該是看 alloc 不是看 init。[NSString stringWithUTF8String:X] 是 class method, 不是 instance method, 所以 caller 自己沒有呼叫到 alloc, 自然也沒義務呼叫 release 搭配 @autoreleasepool 可使用 [X autorelease] 先標記「之後記得 -1」。好處是呼叫方法 A 會傳回新物件時, 可在回傳前呼叫 autorele…