NSTextView 在你背后改变数据

NSTextView changes the data behind your back

我有一个非常简单的玩具应用程序,只有一个文本视图和一个按钮。

这些由 AppController class 控制,看起来像这样

// AppController.h
#import <Cocoa/Cocoa.h>

@interface AppController : NSObject <NSTextViewDelegate>

@property IBOutlet NSTextView *textView;
-(IBAction)apply:(id)sender;

@end

// AppController.m
#import "AppController.h"

@implementation AppController {
    NSString *name;
}

-(void)awakeFromNib {
    name = @"Bob";
    _textView.string = name;
}

-(IBAction)apply:(id)sender {
    name = _textView.string;
    NSLog(@"%@", name);
}

-(void)textDidChange:(NSNotification *)notification {
    NSLog(@"%@", name);
}

@end

当用户在textView中输入一个新值并点击apply按钮时,我想让实例变量名得到这个新值。这就是我第一次单击“应用”按钮时发生的情况。

但下次我更改 textView 中的值时,实例变量会自动更改 无需用户单击“应用”按钮!

这是一种不需要的行为,因为我没有进行任何绑定。事实上,如果我将 textView 更改为 textField,就不会发生奇怪的行为。

所以看起来 NSTextView 做了一些奇怪的非自愿绑定。有人可以解释这种奇怪的行为以及如何解决它。我不希望 textView 在我背后更改我的数据。

我在 YouTube 上制作了一个视频,其中显示了 textField 和 textView 之间的行为差​​异,您可以在 https://youtu.be/qenGKp_L4qs 上查看。

我检查了 Xcode6 和 Xcode5 两者都发生了奇怪的行为。

如果您想在自己的机器上进行测试,请不要忘记将 NSTextView 的委托 属性 与 IB 中的 AppController 连接起来。

这一行:

name = _textView.string;

使您的实例变量引用文本视图在内部使用的同一对象。来自 documentation for the string property of NSTextNSTextView 继承自):

For performance reasons, this method returns the current backing store of the text object. If you want to maintain a snapshot of this as you manipulate the text storage, you should make a copy of the appropriate substring.

当您说 "I do not want the textView to change my data behind my back" 时,您就错了。文本视图正在更改 它的 数据,而您(不明智地)将其视为您自己的数据。

您应该 a) 为您的内部状态创建一个(可能是私有的)属性,b) 在 属性 上使用 copy 属性,以及 c) 使用 self.name = _textView.string; 分配给你的内部状态。

如果您不想这样做,您至少必须使用 name = [_textView.string copy];.

手动复制字符串