NSViewController 在内存管理甚至顶级对象方面都没有做任何特别的事情。它只是提供了一个安全的地方来加载一个 nib,然后在 Nib 的生命周期内将其内容保存在内存中,这意味着该类本身只不过是一个外部文件的所有者。只是为了好玩,我重新实现了这个类,并注释掉了有趣的部分。有些东西,我直接删除了,因为它太老套了,不值得实施,或者没有使用,不值得重新制作。完整的课程,包括文档和评论,可以在这里找到;
@interface CFIViewController : NSResponder <NSCoding> {
@private
NSString *_nibName;
NSBundle *_nibBundle;
id _representedObject;
NSString *_title;
IBOutlet NSView *view;
NSArray *_topLevelObjects;
id _autounbinder;
//NSString *_designNibBundleIdentifier;
}
- (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
这确实是一个非常简单的机制。所有 NSViewController 真正添加到 Cocoa 中任何类型的控制器隐喻的是能够与 NSDocument 一起工作,并且它的底层核心数据非常混乱。
如果视图控制器是 nib 的“文件所有者”,我不是只是将引用周期从我当前的类推送到我的视图控制器吗?NSViewController 做了什么来防止这种情况发生?
NSViewController 以我见过的最有趣的方式之一处理顶级对象的保留。当它获得对包含它们的数组的引用时,它会制作数组的浅拷贝,然后-release
是旧数组的所有对象。实际上,NSViewController 会从 NSCoder 中抢走对 NIB 解冻对象的每个引用,从而保证当数组在-dealloc
.
然而,当涉及到绑定时,NSViewController 有一个名为 NSProxy 子类的内部 getter NSAutounbinder
,KVO 在绑定和解除绑定对象时会查找它。通过调整 release 并为内部 autounbinder 指针提供 getter,控制器类可以毫不费力地释放自己及其绑定。绝对不建议您在未验证 KVO 是否仍在寻找 autounbinder getter 的情况下将 CFIViewController 中的实现用于未来的 OS X 版本,但对于大多数其他版本,它似乎没问题。CFIViewController 提供了在最新提交时使用内部 NSAutoUnbinder 类的选项,从而解决任何绑定保留周期。