1

Xcode で新しい Cocoa アプリケーション プロジェクトを作成し、NSOutlineView オブジェクトと NSTextView オブジェクトをウィンドウに追加しました。これら 2 つのオブジェクトは、MyOutlineView および MyTextView としてサブクラス化されました。その後、2 つのアウトレットを作成し、以下のようなコードを記述しました。

私が見つけた問題は、アプリケーションが実行時に 2 つの異なる MyOutlineView インスタンスを持っていることです。作業中の (有効な) アウトライン ビュー インスタンスは、myOutlineView アウトレット インスタンスと等しくありません。私は何が欠けていますか?

//
//  AppDelegate.h

#import <Cocoa/Cocoa.h>
#import "MyOutlineView.h"
#import "MyTextView.h"

@interface AppDelegate : NSObject <NSApplicationDelegate>

@property (assign) IBOutlet NSWindow *window;
@property (weak) IBOutlet MyOutlineView *myOutlineView;
@property (unsafe_unretained) IBOutlet MyTextView *myTextView;

@end

//
//  AppDelegate.m

#import "AppDelegate.h"

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)n
{
    NSLog(@"AppDelegate.myOutlineView(INVALID)::%@", _myOutlineView);
    NSLog(@"AppDelegate.myTextView::%@", _myTextView);
}

@end

//
//  MyOutlineView.h

#import <Cocoa/Cocoa.h>

@interface MyOutlineView : NSOutlineView <NSOutlineViewDataSource>;

@end

//
//  MyOutlineView.m

#import "MyOutlineView.h"

@implementation MyOutlineView

- (id)initWithCoder:(NSCoder *)aDecoder
{
    // This method is called first.
    self = [super initWithCoder:aDecoder];
    NSLog(@"MyOutlineView initWithCoder(INVALID)::%@", self);
    return self;
}

- (id)initWithFrame:(NSRect)frame
{
    // This method is also called but through a different instance with first one.
    self = [super initWithFrame:frame];
    NSLog(@"MyOutlineView initWithFrame(valid)::%@", self);
    return self;
}

- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
    NSLog(@"MyOutlineView data source delegate(valid)::%@", self);
    return 0;
}

- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
{
    return nil;
}

- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
    return NO;
}

@end

//
//  MyTextView.h

#import <Cocoa/Cocoa.h>

@interface MyTextView : NSTextView

@end

//
//  MyTextView.m

#import "MyTextView.h"

@implementation MyTextView

- (id)initWithCoder:(NSCoder *)aDecoder
{
    // This method is called.
    self = [super initWithCoder:aDecoder];
    NSLog(@"MyTextView initWithCoder::%@", self);
    return self;
}

- (id)initWithFrame:(NSRect)frame
{
    // But this method is NOT called at all.
    self = [super initWithFrame:frame];
    NSLog(@"MyTextView initWithFrame::%@", self);
    return self;
}

@end

出力:

MyTextView initWithCoder::                 [MyTextView: 0x10013be80]
MyOutlineView initWithCoder(INVALID)::     [MyOutlineView: 0x10014bc90]
MyOutlineView initWithFrame(valid)::       [MyOutlineView: 0x1001604a0]
MyOutlineView data source delegate(valid)::[MyOutlineView: 0x1001604a0]
AppDelegate.myOutlineView(INVALID)::       [MyOutlineView: 0x10014bc90]
AppDelegate.myTextView::                   [MyTextView: 0x10013be80]

このため、「AppDelegate.myOutlineView = self;」を配置する必要があります。AppDelegate の関連メソッドを呼び出す場所ならどこでも、MyOutletView の実装に挿入します。それは自然ではないようです。

4

3 に答える 3

2

Xcode では、アウトライン ビューのデリゲートまたはデータ ソースをそれ自体に設定できないようです。

だから私はあなたがこのようなことをしていると推測しています:

ここに画像の説明を入力

つまり、カスタム アウトライン ビュー クラスの 2 番目のコピーをインスタンス化します。

このセットアップからの出力は次のとおりです。

2012-09-26 14:11:34.511 testproj[30255:403] -[MyOutlineView initWithCoder:]
2012-09-26 14:11:34.531 testproj[30255:403] -[MyOutlineView initWithFrame:]

My Outline View の余分な (強調表示された) インスタンスを削除すると、initWithFrame:線が消えます。

アウトライン ビューを独自のデリゲートにするには、代わりに次のようにします。

- (void) awakeFromNib {
    self.delegate = self;
}

つまり、委任パターンのポイントは、サブクラス化の必要性を回避することです。アウトライン ビューのサブクラスが必要な場合は、デリゲート プロトコルを使用する代わりに、NSOutlineView / NSTableView メソッドを直接オーバーライドしてみてください。

于 2012-09-26T18:23:55.030 に答える
1

問題を再現できません。あなたが投稿したすべてのコードをテスト アプリにドロップしましたが、各オブジェクトのインスタンス化は 1 つしか取得できません。試してみると、どちらの initWithFrame メソッドも呼び出されません。私の出力は次のとおりです。

2012-09-26 09:00:38.945 TextViewDoubleInstantiationProblem[451:303] MyTextView initWithCoder::<MyTextView: 0x100123990>
    Frame = {{0.00, 0.00}, {381.00, 182.00}}, Bounds = {{0.00, 0.00}, {381.00, 182.00}}
    Horizontally resizable: NO, Vertically resizable: YES
    MinSize = {381.00, 182.00}, MaxSize = {463.00, 10000000.00}
2012-09-26 09:00:38.953 TextViewDoubleInstantiationProblem[451:303] MyOutlineView initWithCoder(INVALID)::<MyOutlineView: 0x101a1cb90>
2012-09-26 09:00:39.005 TextViewDoubleInstantiationProblem[451:303] AppDelegate.myOutlineView(INVALID)::<MyOutlineView: 0x101a1cb90>
2012-09-26 09:00:39.005 TextViewDoubleInstantiationProblem[451:303] AppDelegate.myTextView::<MyTextView: 0x100123990>
    Frame = {{0.00, 0.00}, {381.00, 182.00}}, Bounds = {{0.00, 0.00}, {381.00, 182.00}}
    Horizontally resizable: NO, Vertically resizable: YES
    MinSize = {381.00, 182.00}, MaxSize = {463.00, 10000000.00}

アプリに表示されていない他のコードはありますか?

于 2012-09-26T16:05:51.443 に答える
0

initWithCoder:nib ファイルで定義されているオブジェクトのロードからの呼び出し。アウトレットの作成について言及しているので、それがあなたが望んでいることだと思います。その場合、initWithFrame:コーダーよりも「無効」である可能性が高いと思います。

ブレークポイントを設定しinitWithFrame:、余分な割り当てを特定するために、その呼び出しがどこから来ているかを追跡します。

于 2012-09-26T11:59:23.187 に答える