NSViewController在内存管理方面并没有什么特别之处,甚至对于顶级对象也是如此。它只是提供了一个安全的地方来加载nib文件,并在整个nib生命周期内将其内容保留在内存中,这意味着该类本身只是一个外部文件的所有者。为了好玩,我重新实现了这个类,并注释掉了一些有趣的部分。有些东西我直接删除了,因为它们太过hacky而不值得实现,或者因为它们很少使用而不值得重新制作。完整的类,包括文档和注释,可以在
这里找到;
@interface CFIViewController : NSResponder <NSCoding> {
@private
NSString *_nibName;
NSBundle *_nibBundle;
id _representedObject;
NSString *_title;
IBOutlet NSView *view;
NSArray *_topLevelObjects;
id _autounbinder;
}
- (id)initWithNibName:(NSString*)nibName bundle:(NSBundle *)nibBundleOrNil;
- (void)setRepresentedObject:(id)representedObject;
- (id)representedObject;
- (void)setTitle:(NSString *)title;
- (NSString *)title;
- (NSView *)view;
- (void)loadView;
- (NSString *)nibName;
- (NSBundle *)nibBundle;
- (void)setView:(NSView *)view;
@end
@implementation CFIViewController
- (void)loadView {
NSArray *topLevelObjects = nil;
NSNib *loadedNib = [[[NSNib alloc]initWithNibNamed:self.nibName bundle:self.nibBundle]autorelease];
if (loadedNib == nil) {
[NSException raise:NSInternalInconsistencyException format:@"-[%@ %@]", NSStringFromClass(self.class), NSStringFromSelector(_cmd)];
return;
}
BOOL loaded = NO;
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8
loaded = [loadedNib instantiateWithOwner:self topLevelObjects:&topLevelObjects];
#else
loaded = [loadedNib instantiateNibWithOwner:self topLevelObjects:&topLevelObjects];
#endif
if (loaded) {
[self _setTopLevelObjects:topLevelObjects];
[topLevelObjects makeObjectsPerformSelector:@selector(release)];
} else {
[NSException raise:NSInternalInconsistencyException format:@"CFIViewController could not instantiate the %@ nib.", self.nibName];
}
if (self.view != nil) {
[self viewDidLoad];
return;
}
[NSException raise:NSInternalInconsistencyException format:@"-[%@ %@]", NSStringFromClass(self.class), NSStringFromSelector(_cmd)];
}
@end
这是一个相当简单的机制。在Cocoa中,NSViewController添加到任何类型的控制器隐喻中的真正功能,是能够与NSDocument及其底层的Core Data混乱有序地工作。
如果视图控制器是nib的“文件所有者”,那么我不会将引用循环从当前类推到我的视图控制器吗?NSViewController如何防止这种情况发生?
NSViewController以最有趣的方式处理顶级对象的保留。当它获取对包含它们的数组的引用时,它会浅复制该数组,然后释放旧数组中的所有对象。实际上,NSViewController夺走了所有对NIB解冻对象的引用,使其远离NSCoder,从而确保在数组在dealloc中消失时安全释放。
但是,在绑定方面,NSViewController具有内部getter用于名为NSAutounbinder的NSProxy子类,KVO在绑定和解除绑定对象时会查找它。通过调整释放并提供内部autounbinder指针的getter,控制器类可以轻松释放自己及其绑定,而无需争斗。绝不建议您在未验证KVO仍然查找autounbinder getter的情况下,在未来的OS X版本中使用CFIViewController中的实现,但对于大多数其他版本,它似乎都可以。CFIViewController提供了使用内部NSAutoUnbinder类的选项,因此解决了任何绑定保留周期问题。
_setTopLevelObjects:
如何对对象进行浅拷贝。它确实对数组进行了拷贝,但没有对其中包含的对象进行拷贝。我有什么遗漏吗? - Monolo