4

私は、アプリではなくバンドルコードベースに取り組んでいます。開発は10.4で開始され、10.4で実行する必要がありますが、10.8までは機能します。手動でnibファイルからビューをロードしますが、nibがバインディングを利用してファイルの所有者に対してバインドし、参照サイクルを作成し、ファイルの所有者クラスの割り当てを解除できないため、かなりのメモリリークがあることに最近気づきました。「ファイルの所有者」に独自のペン先をロードさせることで、これはさらに悪化すると思います。

次のコードを使用してnibをロードします(このコードは基本クラスにあり、サブクラスはオーバーライドします+nibName):

NSString *nibName = [[self class] nibName];
NSNib *nib = [[NSNib alloc] initWithNibNamed:nibName bundle:myBundle];
[nib instantiateNibWithOwner:self topLevelObjects:&topLevelObjects];

10.4をターゲットにする必要があるため、を使用できませんNSViewController。独自のViewControllerクラスを実装する必要があると思いますが、NSViewControllerクラスが約束しているように参照サイクルが発生しないようにするにはどうすればよいですか?ビューコントローラがnibの「ファイルの所有者」である場合、代わりに、現在のクラスからビューコントローラに参照サイクルをプッシュするだけではありませんか?それを防ぐために何をしNSViewControllerますか?

4

2 に答える 2

2

NSViewControllerは、メモリ管理に関して、またはトップレベルのオブジェクトに関してさえ、まったく特別なことは何もしません。これは、ペン先をロードし、その内容をペン先のライフサイクルの間メモリに保持するための安全な場所を提供するだけです。つまり、クラス自体は外部のファイルの所有者にすぎません。キックのためだけに、クラスを再実装し、興味深い部分をコメントアウトしました。いくつかのものは、実装する価値がないほどハッキーだったため、または再作成する必要がないほど未使用だったため、完全に削除しました。ドキュメントとコメントを含む完全なクラスは、ここにあります;

@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を操作する機能であり、その基盤となるCoreDataはまともです。

ビューコントローラがnibの「ファイルの所有者」である場合、代わりに、現在のクラスからビューコントローラに参照サイクルをプッシュするだけではありませんか?NSViewControllerはそれを防ぐために何をしますか?

NSViewControllerは、これまでに見た中で最も興味深い方法の1つで、トップレベルオブジェクトの保持を処理します。それらを含む配列への参照を取得すると、配列のシャローコピーが作成され-release、古い配列のすべてのオブジェクトが作成されます。事実上、NSViewControllerは、NIBの解凍されたオブジェクトへのすべての参照をNSCoderから奪い取り、配列がでなくなったときに安全に解放されることを保証し-deallocます。

NSAutounbinderただし、バインディングに関しては、NSViewControllerには、オブジェクトをバインドおよびバインド解除するときにKVOが検索するという名前のNSProxyサブクラスの内部ゲッターがあります。リリースを微調整し、内部の自動アンバインダーポインターにゲッターを提供することで、コントローラークラスは自分自身とそのバインディングを争うことなく解放できます。KVOが自動アンバインダーゲッターを探していることを確認せずに、将来のOS XリリースでCFIViewControllerの実装を使用することは絶対にお勧めしませんが、他のほとんどのリリースでは問題ないようです。CFIViewControllerは、最新のコミットの時点で内部NSAutoUnbinderクラスを使用するオプションを提供します。これにより、バインディング保持サイクルを解決します。

于 2013-03-27T22:12:24.470 に答える
1

NSWindowControllerまたはカスタムサブクラスを使用することをお勧めします。サブクラスには、viewアウトレットとKVO準拠のrepresentedObjectプロパティを含めることができます。10.4互換の交換にはこれで十分です。

于 2013-03-29T09:22:49.060 に答える