可可真正慢的 NSTextView。有没有更好的方法将文本附加到 NSTextView 的内容中?

发布于 2024-10-28 12:32:56 字数 2989 浏览 1 评论 0原文

我对可可很陌生,这可能是一个完整的新问题。然而我却沮丧得要死。

我有一个非常简单的 Cocoa 应用程序(称为“lines”)来测试将 1000 行文本发送到文本视图。

我从 Xcode 4 开始使用“新的 Cocoa 项目”,并使用所有默认值。这提供了一个空白窗口对象,我可以在其上拖动 IB UI 元素。

我随后构建的 UI 由文本视图和 NIB 文件窗口上的按钮组成。我使用 Xcode 4 将这两个元素拖到 .h 文件中。文本视图连接到 outView 方法,“1000”按钮连接到 one_thousand_button 方法。

The UI

单击按钮“1000”会触发循环打印 1,000 行文本 ("line 1\n" "line 2\n" ... "line 1000") 到名为“outView”的 NSTextView

这是完整的代码(除了描述的 .XIB 文件):

linesAppDelegate.h:

#import <Cocoa/Cocoa.h>

@interface linesAppDelegate : NSObject <NSApplicationDelegate> {
@private
    NSWindow *window;
    NSTextView *outView;
}

@property (assign) IBOutlet NSWindow *window;
@property (assign) IBOutlet NSTextView *outView;
- (IBAction)one_thousand_button:(id)sender;

@end

linesAppDelegate.m:

#import "linesAppDelegate.h"

@implementation linesAppDelegate

@synthesize window;
@synthesize outView;

- (IBAction)one_thousand_button:(id)sender {
    NSString* oldString;
    NSString* newString;

    for(int i=1; i<=1000; i++){
        oldString = [outView string];
        newString = [oldString stringByAppendingString: 
                     [NSString stringWithFormat: @"Line %i\n",i]];
        [outView setString: newString];    
    }
}
@end

这执行起来非常慢。第一次可能需要 7 秒,每按一次“1000”就会变得越来越慢。甚至还有旋转的死亡披萨!

我意识到这可能不是用 1,000 行文本填充 NSTextView 的正确方法,并且读取文本视图内容并使用 stringByAppendingString 方法附加内容的循环是瓶颈。

然而替代方法是什么?

结果

我将此代码包装:

    mach_timebase_info_data_t info;
    mach_timebase_info(&info);
    uint64_t start = mach_absolute_time();

// timed code    

    uint64_t duration = mach_absolute_time() - start;
    duration *= info.numer;
    duration /= info.denom;
    duration /= 1000000;

    NSLog(@"timed code took %lld milliseconds!", duration);

围绕 Adam Preble,我的原创,以及drewk

                 Adam Preble  (Adam, base)     drewk    my pitiful code
 1st run:           3ms          269ms         260ms      1,950ms
 2nd run            3ms          269ms         250ms      2,332ms
 3rd run:           2ms          270ms         260ms      2,880ms

第一次运行添加 1,000 行;第二次运行又添加了 1,000 行,等等。(Adam,base)是他的代码,没有 beginEditingendEditing

很明显,使用 beginEditingendEditing 速度更快!

请参阅有关同步编辑。

I am very new to Cocoa and this is probably a complete newb question. I am frustrated to death however.

I have an extremely simple Cocoa app (called "lines") to test sending 1000 lines of text to a text view.

I started in Xcode 4 with "new Cocoa project" with all the defaults. This gives a blank window object upon which I can drag IB UI elements.

The UI I then constructed consists of a Text View and a button on the window of a NIB file. I am using Xcode 4 to drag those two elements to the .h file. The text view is connected to outView and the "1000" button is connected to one_thousand_button method.

The UI

Clicking the button "1000" triggers a loop to print 1,000 lines of text ("line 1\n" "line 2\n" ... "line 1000") to the NSTextView called "outView"

Here is the entire code (other than the .XIB file described):

linesAppDelegate.h:

#import <Cocoa/Cocoa.h>

@interface linesAppDelegate : NSObject <NSApplicationDelegate> {
@private
    NSWindow *window;
    NSTextView *outView;
}

@property (assign) IBOutlet NSWindow *window;
@property (assign) IBOutlet NSTextView *outView;
- (IBAction)one_thousand_button:(id)sender;

@end

linesAppDelegate.m:

#import "linesAppDelegate.h"

@implementation linesAppDelegate

@synthesize window;
@synthesize outView;

- (IBAction)one_thousand_button:(id)sender {
    NSString* oldString;
    NSString* newString;

    for(int i=1; i<=1000; i++){
        oldString = [outView string];
        newString = [oldString stringByAppendingString: 
                     [NSString stringWithFormat: @"Line %i\n",i]];
        [outView setString: newString];    
    }
}
@end

This is REALLY SLOW to execute. Perhaps 7 seconds the first time and increasingly slow with each press of "1000". Even has the spinning colorful pizza of death!

I realize that this is probably not the right way to fill a NSTextView with 1,000 lines of text and that the loop that read the contents of the text view and appends that with stringByAppendingString method is the bottleneck.

What is the alternative method however?

Result

I wrapped this code:

    mach_timebase_info_data_t info;
    mach_timebase_info(&info);
    uint64_t start = mach_absolute_time();

// timed code    

    uint64_t duration = mach_absolute_time() - start;
    duration *= info.numer;
    duration /= info.denom;
    duration /= 1000000;

    NSLog(@"timed code took %lld milliseconds!", duration);

around the code from Adam Preble, my original, and drewk:

                 Adam Preble  (Adam, base)     drewk    my pitiful code
 1st run:           3ms          269ms         260ms      1,950ms
 2nd run            3ms          269ms         250ms      2,332ms
 3rd run:           2ms          270ms         260ms      2,880ms

The first run adds 1,000 lines; 2nd run adds another 1,000 lines, etc. (Adam, base) is his code without the beginEditing and endEditing

It is clear that using beginEditing and endEditing is WAY faster!

See the Apple documents on Synchronizing Editing.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

内心荒芜 2024-11-04 12:32:56

在对 beginEditingendEditing 的调用中对文本存储进行更新。这会导致 Cocoa 保留所有更改通知,直到您完成对文本存储的更改。

- (IBAction)oneThousandButton:(id)sender
{
    NSTextStorage *textStorage = [outView textStorage];
    [textStorage beginEditing];
    for (int i = 1; i <= 1000; i++)
    {
        NSString *line = [NSString stringWithFormat: @"Line %i\n", i];
        [textStorage replaceCharactersInRange:NSMakeRange([textStorage length], 0)
                                   withString:line];
    }
    [textStorage endEditing];
}

在我的系统上,上述操作大约需要 10 毫秒。如果我注释掉对 beginEditing/endEditing 的调用,大约需要 470ms。

Wrap your updates to the text storage in calls to beginEditing and endEditing. This causes Cocoa to hold all of its change notifications until you have finished making your changes to the text storage.

- (IBAction)oneThousandButton:(id)sender
{
    NSTextStorage *textStorage = [outView textStorage];
    [textStorage beginEditing];
    for (int i = 1; i <= 1000; i++)
    {
        NSString *line = [NSString stringWithFormat: @"Line %i\n", i];
        [textStorage replaceCharactersInRange:NSMakeRange([textStorage length], 0)
                                   withString:line];
    }
    [textStorage endEditing];
}

On my system the above action runs in about 10ms. If I comment out the calls to beginEditing/endEditing, it takes about 470ms.

陌生 2024-11-04 12:32:56

试试这个:

- (IBAction)one_thousand_button:(id)sender {

    for(int i=1; i<=1000; i++){      
         [[[outView textStorage] mutableString] appendString: 
                    [NSString stringWithFormat: @"Line %i\n",i]];
    }
}

这里有一个对 Objective-C 非常宝贵的“快速”参考这里,其中覆盖了 NSTextView 此处。

最好...

Try this:

- (IBAction)one_thousand_button:(id)sender {

    for(int i=1; i<=1000; i++){      
         [[[outView textStorage] mutableString] appendString: 
                    [NSString stringWithFormat: @"Line %i\n",i]];
    }
}

There is an invaluable "quickie" reference to Objective-C HERE with NSTextView covered HERE.

Best...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文