2011年4月28日

[iPhone]Object的擁有權與配置


Object Ownership Policy

每一個物件(object)會有一個或多個的擁有者(owner)。只要物件有擁有者(owner),這個物件就不會消失;若物件沒有擁有者,系統會自行銷毀(destroy)這個物件。

創建即擁有
當你使用alloc, new, copy, mutableCopy時,就代表你創建(create)一個物件(object),也就擁有物件(object)的擁有權
使用retain就能獲得物件的擁有權
一個物件可以擁有一個以上的擁有者。如果你希望一個物件繼續存在,可以使用retain獲得擁有權,讓物件繼續存在。
使用結束須放棄擁有權
可以送出"release"或"autorelease"訊息來放棄擁有權
不能放棄非自身擁有的物件

 


 

Retain Counts

每個物件都可以呼叫"retain count"來計算目前的擁有者數量

當你創建一個物件時,物件的retain count為1

當傳送retain 給物件,retain count會增加1

當傳送release給物件,retain count會減1

當傳送autorelease給物件,代表未來retain count將會減少1

當物件的retain count減少至0的時候,物件就會被dealloc

Autorelease

Autorelease是在NSObject中定義的。送出autorelease訊息,代表你在未來會放棄對物件的擁有權。

可以採用下面這個例子來實做:

– (NSArray *)sprockets

{

NSArray *array = [[NSArray alloc] initWithObjects:mainSprocket,auxiliarySprocket, nil];

return [array autorelease];

}

在這個例子中使用了alloc,因此我們擁有了array的擁有權,並且有「放棄擁有權」的責任。這時候我們就可以使用autorelease。


 

也可以以下面的方式來實做這個function:

– (NSArray *)sprockets

{

NSArray *array = [NSArray arrayWithObjects:mainSprocket,auxiliarySprocket, nil];

return array;

}

這個例子中,我們並沒有得到array的擁有權,因此我們不需要放棄擁有權。


 

Validity of Shared Objects

接收的物件可以在整個function的scope中使用,也可以在此function中回傳這個接受到的物件,且不需擔心物件被release掉。

以下有兩個例外的例子:

1. 把物件從collection中移除

heisenObject = [array objectAtIndex:n];
[array removeObjectAtIndex:n];
// heisenObject could now be invalid.
當一個物件從collection移除的時候,會送出一個release訊息。如果這個collection是物件唯一的擁有者,物件就會被dealloc

2. 當"parent object" 被dealloc

id parent = <#create a parent object#>;
... //
heisenObject = [parent child] ;
[parent release]; // Or, for example: self.parent = nil;
// heisenObject could now be invalid.
當我們release外層的物件,且外層物件是內層物件的唯一擁有者,那內層物件會和外層物件一起被dealloc


 

Accessor Methods

如果你的class中有一個物件變數,你必須確定他不會在你使用的時候被release掉。

當物件被設定的時候,你必須得到這個物件的擁有權,也必須在使用完後放棄擁有權。


 

Deallocating an Object

當retain count為0的時候,物件就會被dealloc,dealloc method會自動的被呼叫。

Dealloc method的目的是把object所擁有的記憶體釋放掉。


 

如果你的class裡面有實例變數,你就必須實做dealloc 方法將這些實例變數release掉,並呼叫父類別的dealloc。

舉例來說,如果我們有兩個實例變數: mainSprocket 和 auxiliarySprocket 。就必須像下面這樣,實做dealloc。

- (void)dealloc {

[mainSprocket release];

[auxiliarySprocket release];

[super dealloc];

}


 

Objects Returned by Reference

有些在Coca定義的object會以位址傳回。

有些method會使用NSError來包含錯誤資訊,像是:

● initWithContentsOfURL:options:error: (NSData)

● initWithContentsOfFile:encoding:error: (NSString)

● executeFetchRequest:error: (NSManagedObjectContext)

如果我們呼叫這些方法,我們並沒有創建NSError物件,所以我們並沒有擁有權,也不需要去release他。

NSString *fileName = <#Get a file name#>;
NSError *error = nil;
NSString *string = [[NSString alloc] initWithContentsOfFile:fileName
encoding:NSUTF8StringEncoding error:&error];
if (string == nil) {
// deal with error ...
{
... //
[string release];

 

Retain Cycles

在某些情況下,兩個object有可能會有循環參照的情形。
舉例來說,考慮一個文字程式的物件關係:
一個Documet object會創建document中的每一頁(Page Object),每一個Page object會有一個實例變數來儲存Page Object是屬於哪一個document object。若Document object retain 了Page object而且Page object也retain 了Document object,到page object release之前 Document object的retain count 不會為0,且Page object也不會被release掉,除非Document被dealloc。
解決方法是,父親物件retain子物件,但是子物件不retain父親物件。

Weak References to Objects

當我們retain一個object時,會產生"強"參考。一個物件擁有強參考是不會被release掉的,強參考決定了一個object的壽命。

在某些情況下,你可能不希望物件不自動dealloc,這時候可以使用"弱"參考。使用pointer來儲存物件產生弱參考。

弱參考可以應用循環參照上。舉例來說,object A和B互相溝通,A和B都需要一個對方的參照,如果A和B互相都retain對方,那麼A和B都不會被dealloc。要打破這種循環參照的方式,其中的一個object必須是另一個object的子object並以弱參考連結另一個object。

以實際的例子來說,在一個階層式的view中, 一個parent view擁有他的child view,但child view並不擁有他的parent,而child view仍然需要知道他的parent view是誰,因此在child內保留一個弱參考到他的parent。
假設有一個物件,你只擁有了一個弱參考指向這個物件,傳送訊息給物件的時候就必須注意。如果你傳送了一個訊息給已經dealloc的物件,應用程式就會crash。你必須明確的定義物件什麼時候有效。

2011年4月20日

[iPhone]讓tabBarController內的viewController回到第一頁


- (void)tabBarController:(UITabBarController *)_tabBarController didSelectViewController:(UIViewController *)viewController 
{
   //取得user點選的Tab index
   NSInteger indexofTab = [_tabBarController.viewControllersindexOfObject:viewController];
   
   if (indexofTab >= 4) //若是Tab大於等於4表示為more button
   {
      [_tabBarController.moreNavigationControllerpopToRootViewControllerAnimated:NO];//讓more回到第一頁
   }
   if ([viewController isKindOfClass:[UINavigationControllerclass]]) //讓navController回到第一頁
   {
      [(UINavigationController*)viewController popToRootViewControllerAnimated:NO];
   }
}