假设您有UIView
显示来自某个模型对象的数据。当模型在后台更改时,它会通过某种订阅机制通知其侦听器;一种非常常见的模式......
我在iOS上做的是订阅ViewControllers
viewWillAppear
中的模型通知。刷新适当的视图以响应更改通知;并在viewWillDisappear
停止订阅。这样,当给定的视图控制器在屏幕外时,确保我不会通过跟踪更改来浪费资源,因此我对此解决方案感到满意。
但是,我当前的项目需要一些跟踪模型对象的视图,并且它们在几个视图控制器中的所有位置都使用。如果我使用以前的方法,则必须在许多视图控制器中复制订阅/取消订阅管道。我想知道,这个逻辑是否可以放在视图中?虽然UIView's
生命周期事件(willMoveToSuperview:和willMoveToWindow :)在这方面有一些模糊的语义,但这必须是可能的,因为Apple正在使用iAd显示视图做什么 - 即ADBannerView没有#&# 39;除了将视频层次结构放入视图层次结构之外,还需要任何管道才能开始显示广告,并且它需要从远程源获取数据,因此不必通过对iAd服务器进行不必要的订阅来浪费资源。
有人做过这件事吗?即可靠地将昂贵的变更跟踪机制与UIView
生命周期事件相结合?
答案 0 :(得分:0)
我使用viewDidAppear
时经常使用此过程,因为我不能确定其他视图控制器在调用当前的viewWillAppear
之前不会调用viewWillDisappear
,这可以将代理分配给某个“共享实例”时会给您带来不便。
无论如何,我总是使用视图控制器来处理这个重新加载,然后调用要刷新的特定视图。有一些特定情况我在removeFromSuperView
方法中取消订阅,但您可以理解这不是最佳方法,因为视图可能会再次作为子视图添加到某个视图中,并且订阅不会自动完成。但是我再次使用它,因为订阅本身是自我保留的,因为订阅最常见的情况是使用计时器或显示链接(这可以通过使用2个类再次避免,但这是另一回事)。
如果在视图控制器级别上使用此订阅/取消订阅,如检查视图是否确实可见,我建议您将其保留在那里并手动订阅/取消订阅视图控制器拥有的视图。如果没有其他原因,您的代码将更易于管理。
如果另一方面,这需要在某个特定视图类型的级别上(创建一个库甚至只是重用),那么我会尝试在某些init
和dealloc
中处理这个问题。方法。再次,如果资源紧张,我会将逻辑移动到视图控制器。
在任何情况下,如果你找到一个可靠的解决方案将这个逻辑严格地放在视图中,我会很高兴听到它。
修改评论以添加自保留解决方案:
当涉及到诸如计时器或通知中心之类的订阅保留类的问题时,您要创建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
中的任何方法,委托或其他任何内容来处理事件。