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 NSText
(NSTextView
继承自):
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];
.
手动复制字符串
我有一个非常简单的玩具应用程序,只有一个文本视图和一个按钮。
这些由 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 NSText
(NSTextView
继承自):
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];
.