2014-10-28 71 views
2

假設您有一個顯示來自某個模型對象的數據的UIView。當模型在後臺更改時,它通過某種訂閱機制通知其偵聽器;一個很常見的模式...用數據提供UIView

我一直在iOS上做的是訂閱ViewControllersviewWillAppear中的模型通知;根據變更通知刷新適當的觀點;並在viewWillDisappear中停止訂閱。這樣,確保了當給定的視圖控制器不在屏幕上時,我不會浪費資源,因此我對此解決方案感到滿意。

但是,我當前的項目需要一些視圖,跟蹤一個模型對象,並且他們遍佈在幾個視圖控制器中。如果我使用先前的方法,那麼訂閱/取消訂閱管道將不得不在許多視圖控制器中重複。我想知道,這個邏輯是否可以放入視圖本身?儘管UIView's生命週期事件(willMoveToSuperview:和willMoveToWindow :)在這方面的語義有些模糊,但這一切都是可能的,因爲這就是Apple對iAd顯示視圖所做的事情 - 即ADBannerView不需要任何管道來開始展示廣告除了將其放入您的視圖層次結構中,並且它從遠程數據源中提取數據,因此不得通過對iAd服務器進行不必要的訂閱來浪費資源。 有沒有人做過這件事?即可靠地將昂貴的變更跟蹤機制與UIView生命週期事件相結合?

回答

0

我用這個程序比較頻繁,雖然使用viewDidAppear,因爲我不能肯定其他一些視圖控制器不前調用viewWillAppear當前一個的viewWillDisappear將被調用,這可能是當你指定一個委託給一些「不便共享實例「。

無論如何,我總是使用視圖控制器來處理這個重新加載,然後調用特定的視圖來刷新。在我註銷removeFromSuperView方法的情況下,有一些特定情況,但您可以理解這不是最好的方法,因爲該視圖可能會再次作爲子視圖添加到某個視圖,並且訂閱不會自動完成。但是,如果視圖本身由於最常見的情況是使用定時器或顯示鏈接(這可以通過使用2個類別,但這是另一回事可以避免的)的訂閱來自我保留,那麼我會再次使用它。

如果此訂閱/取消訂閱在視圖控制器級別上用作檢查視圖是否確實可見,我寧願建議您將它保留在那裏,並手動訂閱/取消訂閱視圖控制器擁有的視圖。如果沒有其他原因,你的代碼將更易於管理。

如果另一方面,這需要在某種特定的視圖類型(創建一個庫,甚至只是重複使用)的水平,那麼我會嘗試在一些initdealloc方法中處理這個問題。再次,如果資源緊張,我會將邏輯移到視圖控制器。

在任何情況下,如果你找到一個堅實的解決方案把這個邏輯嚴格的視圖,我會很高興聽到它。

編輯的評論添加自我保持的解決方案:

當談到當一個類被預訂保留,如定時器或通知中心,你要做的是創造2類問題。一個表示你的接口,並具有獲取特定數據所需的所有方法,如果需要的話包含調用者可以訂閱的一個委託(有一個弱鏈接),我們稱之爲類A.現在這個類包含另一個類,它包含實際訂閱外部來源,如通知中心,並且是自我保留的B類。因此,A類不是自我保留的,因爲它沒有直接訂閱通知中心,定時器......這意味着A類將被正確地解除分配,而B類將堅持並造成潛在的內存泄漏。然後B類確實需要一個明確的呼叫來取消訂閱,因此它被釋放,這應該在類A dealloc方法中完成。

我想簡單的解釋可能有點複雜,所以就看這個代碼:

#import "ClassA.h" 

@class ClassA; 
@class ClassB; 

@protocol ClassBDelegate <NSObject> 
- (void)classBPing:(ClassB *)sender; 
@end 

@interface ClassB : NSObject 
@property NSTimer *timer; 
@property (weak) id<ClassBDelegate> delegate; 
- (void)beginNotificationHandling; 
- (void)endNotificationHandling; 
@end 

@implementation ClassB 
- (void)beginNotificationHandling { 
    if(self.timer == nil) { 
     self.timer = [NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:@selector(onTimer) userInfo:nil repeats:YES]; 
    } 
} 
- (void)endNotificationHandling { 
    [self.timer invalidate]; 
    self.timer = nil; 
} 
- (void)onTimer { 
    [self.delegate classBPing:self]; 
} 

@end 

@interface ClassA()<ClassBDelegate> 
@property ClassB *classBInstance; 
@end 
@implementation ClassA 

- (instancetype)init { 
    if((self = [super init])) { 
     self.classBInstance = [[ClassB alloc] init]; 
     self.classBInstance.delegate = self; 
     [self.classBInstance beginNotificationHandling]; 
    } 
    return self; 
} 

- (void)dealloc { 
    // once this class is deallocated the classB instance must be invalidated so it is deallocated as well 
    [self.classBInstance endNotificationHandling]; 
} 

- (void)classBPing:(ClassB *)sender { 
    NSLog(@"Ping"); 
} 

@end 

注意,這僅僅是源文件,也沒有必要在頭文件中classB,你應該根本不在classA之外使用它。現在使用這個過程,您可以添加classA中的任何方法,委託或其他來處理事件。

+0

當訂閱保留訂閱者時(如NSNotificationCenter這樣做),Init和dealloc就會出現問題 – 2014-10-29 10:06:54

+0

是的,正如我剛纔提到的那樣,當你有一個自我保留的情況時,問題就出現了。讓我編輯答案,添加已經提到的「另一件事」。 – 2014-10-29 11:04:00

+0

試試看這個程序(編輯)... – 2014-10-29 11:22:22