接下來彼得潘實際建立一個簡單的Tab App Test來說明,感謝大畫家朋友張友鷦可愛圖片的友情提供。此App的第一個tab將顯示相愛的甜蜜氣氛,第二個tab則顯示親吻月亮的貓頭鷹。
為了驗證相關method的呼叫,我們將TestFirstViewController.m修改如下:
- (void)viewDidLoad
{
NSLog(@"%s", __PRETTY_FUNCTION__);
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)viewDidUnload
{
NSLog(@"%s", __PRETTY_FUNCTION__);
[super viewDidUnload];
}
- (void)didReceiveMemoryWarning
{
NSLog(@"%s", __PRETTY_FUNCTION__);
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
除此之外,為了確定當記憶體不足時,不在畫面上的UI元件真的被狠心地移除,我們將在TestFirstViewController上的圖片類別設為自訂的MyImageView類別( 繼承UIImageView),並於MyImageView.m裡定義dealloc method。
-(void)dealloc
{
NSLog(@"%s", __PRETTY_FUNCTION__);
}
好了,大功告成,好戲要上演了。先讓我們回到過去,看看iOS 5的表現吧。
iOS 5:
一開始先顯示第1個tab的畫面,此時viewDidLoad被呼叫,log如下。
2012-09-28 00:56:51.808 Test[10727:c07] -[TestFirstViewController viewDidLoad]
切換到第2個tab,並從模擬器觸發記憶體不足情況。(Hardware -> Simulate Memory Warning)
此時log如下:
2012-09-28 00:57:57.826 Test[10727:c07] Received memory warning.
2012-09-28 00:57:57.828 Test[10727:c07] -[TestFirstViewController didReceiveMemoryWarning]
2012-09-28 00:57:57.828 Test[10727:c07] -[MyImageView dealloc]
2012-09-28 00:57:57.829 Test[10727:c07] -[TestFirstViewController viewDidUnload]
根據Log,我們觀察到當didReceiveMemoryWarning呼叫後,不在畫面上的第1個tab的view果然被狠心移除了,因為view上圖片被移除的log -[MyImageView dealloc]鐵證如山般印在log上。而當view整個被移除後,viewDidUnload果然跟著被呼叫了。
要是此時我們再回到第1個tab,由於此tab的view已被移除,它需要重新載入畫面,因此viewDidLoad再度被呼叫。
2012-09-28 01:05:32.063 Test[10727:c07] -[TestFirstViewController viewDidLoad]
但在iOS 6情況就完全不同了,讓我們趕緊接著看下去。
iOS 6:
照著跟剛剛一模一樣的步驟操作,卻發現印出的log精簡許多。
2012-09-28 01:07:35.275 Test[11146:c07] -[TestFirstViewController viewDidLoad]
2012-09-28 01:07:49.983 Test[11146:c07] Received memory warning.
2012-09-28 01:07:49.984 Test[11146:c07] -[TestFirstViewController didReceiveMemoryWarning]
當我們觸發記憶體不足時,didReceiveMemoryWarning如期地被呼叫,當接著呢? 什麼事都沒發生,第一個tab上的view沒被移除,viewDidUnload也不再被呼叫。這是因為在iOS 6系統給我們更高的自主權,讓我們自己決定記憶體不足時要做什麼。如果想要像從前一樣在記憶體不足時移除UI元件節省記憶體,也是可以,只要將TestFirstViewController的 didReceiveMemoryWarning修改如下。
- (void)didReceiveMemoryWarning
{
NSLog(@"%s", __PRETTY_FUNCTION__);
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
if([self isViewLoaded] && self.view.window == nil)
{
NSLog(@"remove view");
self.view = nil;
}
}
說明:
利用isViewLoaded判斷view是否已經load,self.view.window判斷view是否目前不在畫面上,如果這兩個條件都成立,就可以放心大膽地移除view了。(將self.view設為nil)
再次執行:
2012-09-28 01:26:14.369 Test[11924:c07] -[TestFirstViewController viewDidLoad]
2012-09-28 01:26:19.026 Test[11924:c07] Received memory warning.
2012-09-28 01:26:19.028 Test[11924:c07] -[TestFirstViewController didReceiveMemoryWarning]
2012-09-28 01:26:19.029 Test[11924:c07] remove view
2012-09-28 01:26:19.029 Test[11924:c07] -[MyImageView dealloc]
觸發記憶體不足後,由於我們在didReceiveMemoryWarning裡將self.view設為nil,所以第1個tab上的view順利被殺掉了,其上的圖片也一併被解決了。
當我們再次切換回第一個tab時,viewDidLoad也像從前一樣,再一次被呼叫了。
2012-09-28 01:28:14.768 Test[11924:c07] -[TestFirstViewController viewDidLoad]
最後,最重要的,如果我們想要三天三夜不睡覺寫出的App可以同時支援iOS 5和iOS 6,要怎麼做呢?
很簡單,將didReceiveMemoryWarning method定義成剛剛的範例,即可同時支援iOS 5和iOS 6。因為在iOS 5時,觸發記憶體不足後,印出的Log如下:
2012-09-28 19:15:15.703 TestTab[40899:c07] -[TestTabFirstViewController viewDidLoad]
2012-09-28 19:15:20.817 TestTab[40899:c07] Received memory warning.
2012-09-28 19:15:20.818 TestTab[40899:c07] -[TestTabFirstViewController didReceiveMemoryWarning]
2012-09-28 19:15:20.826 TestTab[40899:c07] -[TestTabFirstViewController viewDidUnload]
由Log我們得知, didReceiveMemoryWarning裡if的判斷沒有成立。這是因為在iOS 5當呼叫[super didReceiveMemoryWarning]後,view即會被移除,接著viewDidUnload將被呼叫。等到viewDidUnload執行完後,才會繼續執行didReceiveMemoryWarning裡剩下的程式碼。因此當isViewLoaded被呼叫時,此時view已經被unload,所以isViewLoaded將回傳No。
除此之外,若是想在view被移除時做一些特定的工作,也請記得在iOS 5時要寫在viewDidUnload裡,在iOS 6則寫在didReceiveMemoryWarning裡的self.view = nil;之後。
http://thejoeconwayblog.wordpress.com/2012/10/04/view-controller-lifecycle-in-ios-6/
回覆刪除看完上述的文章,我們原本以為正確的作法似乎又不正確了。