• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: objective-c / cocoa efficiency
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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

  • Follow-Ups:
    • Re: objective-c / cocoa efficiency
      • From: Matthew Johnson <email@hidden>
References: 
 >objective-c / cocoa efficiency (From: "Barton J. Friedland" <email@hidden>)
 >Re: objective-c / cocoa efficiency (From: The Karl Adam <email@hidden>)

  • Prev by Date: Re: objective-c / cocoa efficiency
  • Next by Date: Adding in a timer to a cocoa app
  • Previous by thread: Re: objective-c / cocoa efficiency
  • Next by thread: Re: objective-c / cocoa efficiency
  • Index(es):
    • Date
    • Thread