顯示具有 Mac OS X 標籤的文章。 顯示所有文章
顯示具有 Mac OS X 標籤的文章。 顯示所有文章
0

Delegate

| 11/26/2011
Delegation 是物件導向 Design Pattern 的一種。

這種設計原則的作法,是讓 reciver 授權委託給 delegatee 物件來處理所接收到的訊息。如此一來,就可以模糊化 reciver 介面設計。這種設計方法大量被 Cocoa Framework 所使用,原因就在於將所屬職責委託出去,可以減少 reciver 負擔工作的種類,並且可以因為 delegatee 的設計的適應各種不同的狀況,增加程式設計上面的彈性。

在這裡舉一個例子:今天,SDK 裡面設計了表格物件。表格大概是我們最常見到用來分類、統整項目的方法。可是,根據需求不同,表格顯示資料的過濾方式、算法、資料的取得都會有所不同。設計表格物件的設計師不可能有辦法先把所有的狀況建立在表格物件裡面。所以設計便成為,當表格需要顯示資料的時候,它會向表格所授權的 delegatee 要求、取得資料之後,再自行顯示。

下面用一個比較簡單的實作例子,我們要求一個區塊物件,去繪製一個圖型:

@protocol FrameDelegate
@required
- (void)draw;
@end

@interface Frame : NSObject {
    uint32 bound_x;   // 相對整個畫面的座標
    uint32 bound_y;
    uint32 width;    // 框架的長、寬
    uint32 height;

    id delegate;
}
- (void)draw; 
- (void)setDelegate:(id <FrameDelegate>)obj;
@end

我們可以看到,Frame 物件具有一個方法:draw。這個方法可以在框架所決定的區塊裡面繪製東西,可是我們並不知道到底要會製成什麼東西,所以我們就建立兩個物件:

Shape <----- Circle
Shape <----- Square

我們這裡,Shape 可以當做是一個母物件,並且遵守 FrameDelegate 協定,必須要有 draw 方法。如此一來,框架的實作本身就可以很簡單的:

@implementation
... (do something)

- (void)draw {
// some work here
    [delegate draw];
}
... (do something)
@end



在實際應用上面,就成了以下這個樣子:

Frame *f1;
Frame *f2;
Circle *c;
Square *s;

[f1 setDelegate:c];
[f2 setDelegate:s];

[f1 draw];   // 畫出圓形
[f2 draw];   // 畫出方形


Delegate 是 Cocoa Framework 裡面常用的技法,這得要記得喔!
0

Retain Count ~ Auto Release Pool

| 11/23/2011
Retain Count

繼上一篇文章所述,Retain Count 是我們管理 Objective-C 程式記憶體使用最重要的一環。但是我們不難發現,其實如果真的所有的東西都必須經過 alloc、init、retain、release 的動作,會讓整篇程式上上下下充斥著這些程式碼,反而降低了整個程式的閱讀性、也提高了出錯的危險。這裡,Auto Release Pool 這個設計就出現了。從字面上看來,它可以自動的幫我們釋放掉記憶體,但其實它並沒有像是垃圾回收那樣複雜的演算法存在。它其實是一個聰明、卻又單純、簡單的設計,並且解決上面所述造成程式碼撰寫的複雜度、並且同時提高程式在編成的時候的彈性。

我們想像記憶體的空間是個游泳池,而建立的物件實體就像是游泳池的遊客。遊客在游泳池開門之後開始戲水,當游泳池要打烊的時候,遊客就會離開。

這個想法運用在這邊就是,我們先建立一個 NSAutoreleasePool 的物件。之後,我們只要在所有自己建立的物件實體,呼叫方法 -(void)autorelease,便可以讓該物件與 AutoreleasePool 做連結。Pool 也不做什麼複雜計算、搜尋,只是單純的把該物件實體記錄下來。當 Pool 被 Release 掉的時候,Pool 便將所有跟他連結在一起的物件,全部個別的呼叫「一次」-(void)release。如此一來,該物件就不需要擔心,什麼時候該釋放它,Pool 會幫我們管好一切。

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];


OBJ *o1 = [[[OBJ alloc] init] autorelease];
OBJ *o2 = [[[OBJ alloc] init] autorelease];


// Do Something


[pool release];


這個機制最重要的運用是,由於所有的 Cocoa 應用程式都至少會建立一個 Autorelease Pool。我們可以開始取得一些「暫時物件」的實體,並且讓系統在適當的時候,自己釋放掉那些暫時物件。最好的例子莫過於字串。我們可以用以下的程式碼取得一個字串物件的實體:

NSString *str = @"Some Text Message";
NSString *str2 = [NSString stringWithFormat:@"Number: %d", 50];

上面兩種取得字串的方式,便是包含了 Auto Release 機制在的。

由於系統本身已經預先建立好一個 Autorelease Pool,所以當後者 @"Some Text Message"; 被使用的時候,系統便會使用 alloc->init->autorelease 一連串的方法建立一個物件實體,並且把實體的位置回傳。我們不需要額外的程式碼便可以得到想要的物件。這個原則在物件、實體方法 (methods) 回傳值的時候非常的有用。使用者使用你所設計的方法取得的實體並不需要在多花時間去考慮是否要 release。

但是,這時又會產生一個疑問:相同的都是取得實體,我該怎麼知道哪些是需要 release,哪些不需要?沒錯,這確實是一件很重要的事情。因為當一個物件實體的 Retain Count 降為 0 之後,它就會呼叫 dealloc,並且摧毀。從此之後,如果又有人(不論是你自己,還是 Autorelease Pool)呼叫了實體方法 release,便會因為實體根本不存在而導至崩潰。

要知道究竟是哪一個物件需要手動 release,我們只需要掌握一條原則:所有權在你自己身上的,你自己釋放,其餘的交給 Autorelease Pool。什麼樣的實體,所有權會在自己身上?很簡單。你取得實體的方法,具有幾個關鍵字:init、retain、copy。只要是其中一項,你便具有該物件的所有權,你就得要自己手動 release。

清楚了嗎?
2

Self assign IP address

| 2/08/2011
解了一大堆 Windows 的怪問題,今天換個口味,來解一下 Mac 上面的怪問題。這個問題看起來似乎是從 Leopard 開始的,當然我不知道確定的時間,但是就是有這個問題。
當我們使用 AirPort 直接連結無線基地台的時候,在絕大多數的情況下都是使用 DHCP 來取得 IP Address。取得 IP 這個動作在 AirPort 是有設定一個 Timeout 的時間在的。當 Timeout 過了之後,系統將會自動設定一個 168.* 的 IP 給你,因為被分配到一個不屬於該 AP 的網域之下,所以你的電腦將沒有辦法連上網路。

仔細去分析發生這個問題的癥結:Timeout,可以發現,發生這種事情可能的理由就是該 AP 的網路連線上面有問題,而另外一個最重要的理由就是防火牆!或許大家會覺得不可思議,為什麼防火牆會把 DHCP 給擋在門外?或許沒有人可以給我們一個正確的答案,甚至有的時候你防火牆沒有打開,卻也會發生這種現象。解決的辦法其實也很簡單,只要你在安全性設定裡面把防火牆先「打開」再「關閉」,這個問題便會迎刃而解!