Re: objective-c / cocoa efficiency
Re: objective-c / cocoa efficiency
- Subject: Re: objective-c / cocoa efficiency
- From: b a r t o n <email@hidden>
- Date: Tue, 8 Mar 2005 15:31:16 -0800
Here is the implementation diff - tell me what you think.
On Tue, 8 Mar 2005 17:46:43 -0500, The Karl Adam <email@hidden> wrote:
> We would need to see some of the code you submitted, but if teh
> -isEqualToString comment is any indication, then this developer is
> sacrificing a great deal of functionality for pretty nominal speed
> increases. In this case he is sacrificing support for multibyte
> strings and smart comparisons of accented chars for a few
> milliseconds. This is not to say that the isEqualToString: method is
> the fastest in teh world, but rather it has an overwhelming amount of
> functionality to make up for its lack of raw speed.
>
> If this developer truly feels this way his project will be plagued
> with weird functionality and bugs that have long been stomped out by
> the mature Cocoa apis, not to mention the amount of time he will spend
> tracking down silly bugs that are there simply because he didn't want
> to use the code of people that have much more experience on this sort
> of thing.
>
> If you don't mind, can you share the code and or project that you are
> attempting to work with?
>
> -Karl
>
> On Tue, 8 Mar 2005 14:18:53 -0800, Barton J. Friedland
> <email@hidden> wrote:
> > I am working on an open source project where the UI is written in Cocoa.
> > It therefore seemed natural to me to use as many of he Cocoa objects as
> > possible, since it appears from the detailed documentation that a
> > significant number of person-years has gone into the development of
> > these frameworks. I would therefore expect them to not only be
> > reliable, but efficient.
> >
> > It seems, however, that the project manager of the application I
> > contributed code on thinks differently (pardon the pun).
> >
> > Here are some of his comments:
> >
> > "I am not going to accept something that causes a thousand garbage
> > objects to be collected in one event cycle.
> > Not optimizing where speed is not a concern is one thing. flagrant
> > inefficiency is something else."
> >
> > "It isn't just the typing response. The main thread is used for updating
> > the UI and things scheduled with AppClock, so it is really unfriendly
> > to other tasks to be throwing away CPU cycles like you had an infinite
> > number to burn."
> >
> > "And so you are using a NSString isEqualToString: which can handle
> > every script known to man and has to be aware of encodings instead of
> > simply using a switch statement and == which is probably at least a
> > hundred times faster per character."
> >
> > ---
> >
> > Can someone enlighten me here? I have written my code as per the Apple
> > guidelines as I understand them. Is there some secret horrible
> > inefficiency with Cocoa and / or Objective-C that I should know about
> > in order to satisfy the needs of this type of programmer?
> >
> > Also, what kind of profiling tools could I use to prove these statements
> > as either true or false?
> >
> > Any help or information would be appreciated.
> >
> > Thanks,
> >
> > Barton
> >
> > _______________________________________________
> > Do not post admin requests to the list. They will be ignored.
> > Cocoa-dev mailing list (email@hidden)
> > Help/Unsubscribe/Update your Subscription:
> >
> > This email sent to email@hidden
> >
>
diff -Naur /Users/barton/Projects/sc3_orig/SuperCollider3/source/app/CocoaFilePrimitives.M /Users/barton/Projects/sc3+/SuperCollider3/source/app/CocoaFilePrimitives.M
--- /Users/barton/Projects/sc3_orig/SuperCollider3/source/app/CocoaFilePrimitives.M Thu Aug 26 14:45:07 2004
+++ /Users/barton/Projects/sc3+/SuperCollider3/source/app/CocoaFilePrimitives.M Wed Mar 2 10:33:14 2005
@@ -29,6 +29,7 @@
#import "PyrObject.h"
#import "PyrKernel.h"
#import "VMGlobals.h"
+#import "Stack.h"
#import "MyDocument.h"
#import "GC.h"
diff -Naur /Users/barton/Projects/sc3_orig/SuperCollider3/source/app/GUIPrimitives.M /Users/barton/Projects/sc3+/SuperCollider3/source/app/GUIPrimitives.M
--- /Users/barton/Projects/sc3_orig/SuperCollider3/source/app/GUIPrimitives.M Thu Feb 10 02:20:15 2005
+++ /Users/barton/Projects/sc3+/SuperCollider3/source/app/GUIPrimitives.M Wed Mar 2 10:29:08 2005
@@ -25,6 +25,7 @@
#include "PyrKernel.h"
#include "VMGlobals.h"
#include "SC_RGen.h"
+#include "Stack.h"
#import "MyDocument.h"
#import "SCGraphView.h"
#import "SCVirtualMachine.h"
diff -Naur /Users/barton/Projects/sc3_orig/SuperCollider3/source/app/MyDocument.M /Users/barton/Projects/sc3+/SuperCollider3/source/app/MyDocument.M
--- /Users/barton/Projects/sc3_orig/SuperCollider3/source/app/MyDocument.M Thu Feb 10 02:11:38 2005
+++ /Users/barton/Projects/sc3+/SuperCollider3/source/app/MyDocument.M Mon Mar 7 08:58:57 2005
@@ -19,6 +19,7 @@
*/
#import <Cocoa/Cocoa.h>
+#import "Stack.h"
#import "MyDocument.h"
#import "SCTextView.h"
#import "SCVirtualMachine.h"
@@ -61,6 +62,12 @@
scrollView = nil;
isRichText = YES;
promptToSave = YES;
+ //begin code added by barton (music at netspheres.net) on 02 March 2005
+ lastIndex = 0;
+ [self initStrings];
+ [self initDictionaries];
+ [self initCharacterSets];
+ //end code added by barton (music at netspheres.net) on 02 March 2005
return [super init];
}
@@ -890,6 +897,12 @@
- (void)dealloc{
//for some reason some Documents created with open do not call windowWillClose
//so that its action is called here: jan.t
+
+ //begin code added by barton (music at netspheres.net) on 02 March 2005
+ //NSLog(@"dealloc");
+ [self deallocCharacterSets];
+ [self deallocDictionaries];
+ //end code added by barton (music at netspheres.net) on 02 March 2005
[super dealloc];
}
- (IBAction) becomePostWindow: (id) sender
@@ -1029,6 +1042,17 @@
[[[self textView] window] setAlphaValue: [color alphaComponent]];
}
+//begin code added by barton (music at netspheres.net) on 02 March 2005
+- (void) setMyString:(NSString *)value
+{
+ if(myString != value)
+ {
+ [myString release];
+ myString = [value copy];
+ }
+}
+//end code added by barton (music at netspheres.net) on 02 March 2005
+
- (BOOL)promptToSave
{
return promptToSave;
@@ -1047,6 +1071,390 @@
- (void) mouseDown: (NSEvent*) event
{
}
+
+//begin code added by barton (music at netspheres.net) on 02 March 2005
+// NSTextView Delegate Methods
+- (BOOL)textView:(NSTextView *)textView shouldChangeTextInRange:(NSRange)affectedCharRange replacementString:(NSString *)replacementString
+{
+ NSString *leftChar;
+
+ highlightIndex = -1;
+ //there should be a way to reduce this code to call the specific searchForMatchingCharacters function
+ if([self isRightBrace: replacementString])
+ {
+ leftChar = [self lookupMatchingLeftBraceFor: replacementString];
+ highlightIndex = [self searchForMatchingCharacter: leftChar
+ fromIndex: affectedCharRange.location];
+ if(highlightIndex != -1)
+ {
+ [NSThread detachNewThreadSelector:@selector(sequenceBraceHighlighting)
+ toTarget:self
+ withObject: nil];
+ }
+ else
+ {
+ NSBeep();
+ //return NO;
+ //TODO: nice to turn this on once we have the ability to identify code in comments and quotes
+ }
+ }
+ return YES;
+}
+
+// --------------------------------
+// Internal Text Management Methods
+// --------------------------------
+
+/* this method is designed to take as parameters a left brace character to match
+and a starting index position. it then works backwards through the string in
+the textview looking for the matching left bracket. if it finds one, it returns
+the index position of the matching brace character, otherwise returning -1.
+*/
+- (int)searchForMatchingCharacter:(NSString *)LeftBraceToMatch fromIndex: (unsigned) fromIndex
+{
+ // copy text to local NSString
+ [self setMyString:[textView string]];
+ //NSLog(@"Length of string is now %d", [myString length]);
+ //NSLog(@"I was passed index %d", fromIndex);
+
+ // set up variables for search
+ NSString *toTest;
+ NSString *toMatch;
+ NSRange extractionRange;
+ int i;
+ int returnVal = -1;
+ Stack *stack;
+ BOOL popped;
+
+ toMatch = LeftBraceToMatch;
+ stack = [[Stack alloc] init]; //used to keep track of any nested braces
+
+ // set up for loop to iterate backward through the string to see if we find a matching character
+ for(i = (fromIndex - 1); i >= 0; i--)
+ {
+ extractionRange = NSMakeRange(i, 1);
+ toTest = [myString substringWithRange: extractionRange];
+ //first look for right brackets
+ if([self isRightBrace: toTest])
+ {
+ //NSLog(@"Found (possibly nested) right brace at index %d", i);
+ [self lookupDictionaryValueForRightBrace: toTest andPushValueOn: stack];
+ }
+ //now check for matching left bracket
+ if([toMatch isEqualToString: toTest])
+ {
+ //NSLog(@"possible matching left character found at index %d", i);
+ if([stack isEmpty])
+ {
+ //NSLog(@"stack is empty so this is the matching left brace");
+ [stack free];
+ return i;
+ }
+ }
+ //now check for all other left brackets
+ if([self isLeftBrace: toTest])
+ {
+ //if the stack is empty, then this is a non-matching left brace
+ if([stack isEmpty])
+ {
+ //NSLog(@"invalid left brace '%@' at position %d", toTest, i);
+ [stack free];
+ return -1;
+ }
+ //otherwise the stack is not empty and we test to see if it is matching
+ //NSLog(@"popping dictionary value of left brace '%@' found at index %d on stack", toTest, i);
+ popped = [self lookupDictionaryValueForLeftBrace: toTest andPopValueFrom: stack];
+ if(! popped)
+ {
+ //NSLog(@"invalid left brace '%@' at position %d", toTest, i);
+ [stack free];
+ return -1;
+ }
+ }
+ //failure case
+ if((i == 0) && (![toMatch isEqualToString: toTest]))
+ {
+ //NSLog(@"No match found.");
+ returnVal = -1;
+ }
+ }
+ [stack free];
+ return returnVal;
+}
+
+/* helper method that returns a NSNotFound NSRange when required */
+- (NSRange)notFound
+{
+ return NSMakeRange(NSNotFound , 0);
+}
+
+/* this method is designed to take as a parameter an index position
+which is then uses to create an NSRange and temporarily highlight
+in the corresponding NSTextView. for decomposition purposes, it
+returns the original position so that the selection can be reset
+in a later call.
+*/
+- (void)highlightLeftCharacter: (unsigned) atPosition
+{
+ //set range for character to highlight
+ NSRange toHighlight = NSMakeRange(atPosition,1);
+ //highlight the character
+ [textView drawHighlightRange: toHighlight];
+}
+
+/* used as part of sequence brace highlighting - causes the thread to sleep
+ for a short time */
+- (void)sleepForAMoment
+{
+ //get a reference to the current thread so we can sleep for a moment
+ NSTimeInterval sleepInterval = 0.65;
+ NSDate *wakeTime = [NSDate dateWithTimeIntervalSinceNow: sleepInterval];
+ [NSThread sleepUntilDate: wakeTime];
+}
+
+/* this method is designed to put the selection back
+(think "put the candle back" from "young frankenstein")
+the way it was before we changed it. */
+- (void)setSelectionToOriginalPosition: (unsigned)atPosition
+{
+ NSRange originalPosition = NSMakeRange(atPosition,0);
+ [textView scrollRangeToVisible: originalPosition];
+ [textView setSelectedRange: originalPosition];
+}
+
+/* this method sequences together the brace highlighting
+methods in the correct order so that they can be called
+from one place. note: we also place a lock on this section
+of code so it can run atomically, placing any user keystrokes
+on hold... */
+- (void)sequenceBraceHighlighting
+{
+ /* since this method is intended to be called by another
+ thread we establish an autorelease pool */
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [self highlightLeftCharacter: highlightIndex];
+ [self sleepForAMoment];
+ [textView removeHighlighting];
+ [pool release];
+}
+
+//brace matching functions
+//------------------------
+
+- (void)initStrings
+{
+ rightBracket = @"]";
+ rightCurlyBracket = @"}";
+ rightParenthesis = @")";
+ leftBracket = @"[";
+ leftCurlyBracket = @"{";
+ leftParenthesis = @"(";
+}
+
+- (void)initDictionaries
+{
+ static const int N_ENTRIES = 3;
+ NSString *keyArray[N_ENTRIES];
+ NSNumber *valueArray[N_ENTRIES];
+ int i;
+
+ //populate valueArray
+ for(i = 0; i < N_ENTRIES; i++)
+ {
+ valueArray[i] = [NSNumber numberWithInt: i];
+ }
+
+ //create rightBraces dictionary first
+ keyArray[0] = rightParenthesis;
+ keyArray[1] = rightCurlyBracket;
+ keyArray[2] = rightBracket;
+
+ rightBraces = [NSDictionary dictionaryWithObjects:(id *)valueArray
+ forKeys:(id *) keyArray
+ count: N_ENTRIES];
+
+ [rightBraces retain];
+
+ //now create leftBraces dictionary
+ keyArray[0] = leftParenthesis;
+ keyArray[1] = leftCurlyBracket;
+ keyArray[2] = leftBracket;
+
+ leftBraces = [NSDictionary dictionaryWithObjects:(id *)valueArray
+ forKeys:(id *) keyArray
+ count: N_ENTRIES];
+
+ [leftBraces retain];
+}
+
+- (void)deallocDictionaries
+{
+ [rightBraces release];
+ [leftBraces release];
+}
+
+- (void)initCharacterSets
+{
+ NSMutableCharacterSet *workingSet;
+ NSMutableCharacterSet *completeSet;
+
+ workingSet = [[NSMutableCharacterSet alloc] init];
+ completeSet = [[NSMutableCharacterSet alloc] init];
+ [workingSet addCharactersInString:@"({["];
+ leftBraceSet = [workingSet copy];
+ completeSet = [workingSet copy];
+ [workingSet release];
+
+ workingSet = [[NSMutableCharacterSet alloc] init];
+ [workingSet addCharactersInString:@")}]"];
+ [completeSet addCharactersInString:@")}]"];
+ rightBraceSet = [workingSet copy];
+ allBraceSet = [completeSet copy];
+ [workingSet release];
+ //TODO: is this autorelease?
+ [completeSet release];
+
+}
+
+- (void)deallocCharacterSets
+{
+ [leftBraceSet release];
+ [rightBraceSet release];
+ //TODO: is this autorelease?
+ [allBraceSet release];
+}
+
+/* this function is used to determine if a given character
+is in the rightBraces NSDictionary. */
+- (BOOL)isRightBrace : (NSString *) testChar
+{
+ id result = [rightBraces objectForKey: testChar];
+ //if it if not null then it exists so return true
+ if(result != nil)
+ return YES;
+ return NO;
+}
+
+/* this function is used to determine if a given character
+is in the LeftBraces NSDictionary. */
+- (BOOL)isLeftBrace : (NSString *) testChar
+{
+ id result = [leftBraces objectForKey: testChar];
+
+ //if it if not null then it exists so return true
+ if(result != nil)
+ return YES;
+ return NO;
+}
+
+/* given a right brace, looks up the corresponding left brace. */
+- (NSString *)lookupMatchingLeftBraceFor : (NSString *) rightBrace
+{
+ // we only allow valid characters to be looked up
+ if(! [self isRightBrace: rightBrace])
+ [self throwRightBraceException];
+ // get value from dictionary
+ id myValue = [rightBraces objectForKey: rightBrace];
+ // use value to get key for left brace
+ NSArray *keys = [leftBraces allKeysForObject: myValue];
+ //exception checking - there should only be one key for each value
+ if([keys count] != 1)
+ [self throwBraceDictionaryException: @"lookupMatchingLeftBraceFor"];
+ //cast the left brace key into a string object
+ return (NSString *)[keys objectAtIndex: 0];
+}
+
+/* given a left brace, looks up the corresponding right brace. */
+- (NSString *)lookupMatchingRightBraceFor : (NSString *) leftBrace
+{
+ // we only allow valid characters to be looked up
+ if(! [self isLeftBrace: leftBrace])
+ [self throwLeftBraceException];
+ // get value from dictionary
+ id myValue = [leftBraces objectForKey: leftBrace];
+ // use value to get key for right brace
+ NSArray *keys = [rightBraces allKeysForObject: myValue];
+ //exception checking - there should only be one key for each value
+ if([keys count] != 1)
+ [self throwBraceDictionaryException: @"lookupMatchingRightBraceFor"];
+ //cast the left brace key into a string object
+ return (NSString *)[keys objectAtIndex: 0];
+}
+
+/* given a right brace, looks up the dictionary value and pushes it on the stack */
+- (void)lookupDictionaryValueForRightBrace : (NSString *) rightBrace andPushValueOn : (Stack *)stack
+{
+ if(! [self isRightBrace: rightBrace])
+ [self throwRightBraceException];
+ //get value from dictionary
+ id myValue = [rightBraces objectForKey: rightBrace];
+ [stack push:(int)myValue];
+}
+
+/* given a left brace, looks up the dictionary value and pops it from the stack - returns false if
+ the value on the top of the stack does not match (indicating mismatched braces) */
+- (BOOL)lookupDictionaryValueForLeftBrace : (NSString *) leftBrace andPopValueFrom : (Stack *)stack
+{
+ if(! [self isLeftBrace: leftBrace])
+ [self throwLeftBraceException];
+ //get value from dictionary
+ id myValue = [leftBraces objectForKey: leftBrace];
+ if((int)myValue == [stack getTop])
+ {
+ [stack pop];
+ return YES;
+ }
+ return NO;
+}
+
+- (unsigned)indexOfRightmostCharacter: (NSRange) from
+{
+ return from.length - 1;
+}
+
+// utility methods for throwing common exceptions
+// **********************************************
+
+- (void)throwRightBraceException
+{
+ NSException *rightBraceException = [NSException exceptionWithName:@"Right brace exception"
+ reason:@"a right brace was expected where there was none"
+ userInfo:nil];
+ [rightBraceException raise];
+
+}
+
+- (void)throwLeftBraceException
+{
+ NSException *leftBraceException = [NSException exceptionWithName:@"Left brace exception"
+ reason:@"a left brace was expected where there was none"
+ userInfo:nil];
+ [leftBraceException raise];
+
+}
+
+// the caller info should be something like the name of the calling method
+- (void)throwBraceDictionaryException : (NSString *)callerInfo
+{
+ NSString *standardMessage = @" : we should get back only one key from the right braces dictionary for a given value";
+ NSString *completeMessage = [standardMessage stringByAppendingString: callerInfo];
+
+ NSException *dictionaryException = [NSException exceptionWithName:@"Brace Dictionary exception"
+ reason: completeMessage
+ userInfo:nil];
+ [dictionaryException raise];
+}
+
+- (void)throwInvalidRangeException : (NSString *) theReason
+{
+ NSException *invalidRangeException = [NSException exceptionWithName:@"Invalid range exception"
+ reason: theReason
+ userInfo:nil];
+ [invalidRangeException raise];
+}
+
+//end code added by barton (music at netspheres.net) on 02 March 2005
+
@end
@@ -1103,4 +1511,3 @@
}
[window makeKeyAndOrderFront: nil];
}
-
diff -Naur /Users/barton/Projects/sc3_orig/SuperCollider3/source/app/SCTextView.M /Users/barton/Projects/sc3+/SuperCollider3/source/app/SCTextView.M
--- /Users/barton/Projects/sc3_orig/SuperCollider3/source/app/SCTextView.M Thu Feb 10 02:16:20 2005
+++ /Users/barton/Projects/sc3+/SuperCollider3/source/app/SCTextView.M Mon Mar 7 08:56:21 2005
@@ -19,6 +19,7 @@
*/
#import "SCTextView.h"
+#import "Stack.h"
#import "MyDocument.h"
#include <pthread.h>
@@ -245,5 +246,38 @@
}
pthread_mutex_unlock (&gLangMutex);
}
+
+//begin code added by barton (music at netspheres.net) on 02 March 2005
+
+- (void)removeHighlighting
+{
+ textLength = [[self textStorage] length];
+ textCharRange = NSMakeRange(0, textLength);
+ [layoutManager removeTemporaryAttribute: NSBackgroundColorAttributeName
+ forCharacterRange: textCharRange];
+}
+
+- (void)drawHighlightRange: (NSRange)highlightRange
+{
+ if(highlightRange.location == NSNotFound)
+ return;
+ //we could set the color from a preference at some later point
+ [layoutManager addTemporaryAttributes: [NSDictionary dictionaryWithObjectsAndKeys: [NSColor lightGrayColor], NSBackgroundColorAttributeName, nil] forCharacterRange: highlightRange];
+}
+
+- (id)initWithFrame: (NSRect)frameRect textContainer: (NSTextContainer *)aTextContainer
+{
+
+ if (self = [super initWithFrame: frameRect
+ textContainer: aTextContainer])
+ {
+ layoutManager = [self layoutManager];
+ textContainer = [self textContainer];
+ }
+ return self;
+}
+//end code added by barton (music at netspheres.net) on 02 March 2005
+
+
@end
diff -Naur /Users/barton/Projects/sc3_orig/SuperCollider3/source/app/SCVirtualMachine.M /Users/barton/Projects/sc3+/SuperCollider3/source/app/SCVirtualMachine.M
--- /Users/barton/Projects/sc3_orig/SuperCollider3/source/app/SCVirtualMachine.M Thu Feb 10 01:25:16 2005
+++ /Users/barton/Projects/sc3+/SuperCollider3/source/app/SCVirtualMachine.M Wed Mar 2 10:32:48 2005
@@ -20,6 +20,7 @@
#import "SCVirtualMachine.h"
+#import "Stack.h"
#import "MyDocument.h"
#import "SCGraphView.h"
#include "ChangeCounter.h"
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden