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 程式另有使用指標位置位移的技巧取得額外資訊, 附上 Scott 的原文:

C 程式中解這種『從既有 data type 查額外資訊』,有個不太直覺的解法, container_of() : http://linuxkernel51.blogspot.tw/2011/02/how-containerof-macro-works-example.html

struct MyCustomType {
    int misc;
    struct ExistingType m0;
    void *data;
};
 
void my_callback(struct ExistingType *e)
{
   struct MyCustomType *p = containter_of(e, struct MyCustomType, m0);
   /* access p->data ... */
}
 
/* See http://www.kroah.com/log/linux/container_of.html
   for how the container_of() macro is implemented. */

這個片語是在 MyCustomType 內嵌一個 ExistingType,然後當你確定手上的 (ExistingType *) 來源時就能丟進 cotainer_of() 查回 MyCustomType。有點 『既然我用的程式語言支援 pointer arithmetic 那我就盡量用』的感覺。Linux kernel 內的連結串列就是用這個片語寫的 http://kernelnewbies.org/FAQ/LinkedLists

留言

這個網誌中的熱門文章

(C/C++ ) 如何在 Linux 上使用自行編譯的第三方函式庫

熟悉系統工具好處多多

virtualbox 使用 USB 裝置