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.
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.
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.
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...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With