1

我在 Xcode 中创建了一个新的 Cocoa 应用程序项目,然后将一个 NSOutlineView 和一个 NSTextView 对象添加到窗口中。这两个对象被归类为 MyOutlineView 和 MyTextView。之后,我为他们做了两个出口并编写了如下代码。

我发现问题是应用程序在运行时有两个不同的 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;” 进入 MyOutletView 的实现,无论它调用 AppDelegate 的相关方法。这似乎不自然。

4

3 回答 3

2

Xcode 似乎不允许您将大纲视图的委托或数据源设置为自身。

所以我猜你正在做这样的事情:

在此处输入图像描述

也就是说:实例化自定义大纲视图类的第二个副本。

这是此设置的输出:

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

通过删除我的大纲视图的额外(突出显示)实例,该initWithFrame:行消失了。

要使大纲视图成为自己的委托,请改为执行以下操作:

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

也就是说,委托模式的重点是避免子类化的需要。如果您确实需要大纲视图子类,请尝试直接覆盖 NSOutlineView / NSTableView 方法,而不是使用委托协议。

于 2012-09-26T18:23:55.030 回答
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 回答