Cocoa Embedded Applescript Lag

Discussion in 'Mac Programming' started by Darkroom, Nov 30, 2008.

  1. Darkroom Guest

    Darkroom

    Joined:
    Dec 15, 2006
    Location:
    Montréal, Canada
    #1
    i've been playing around with applescript in cocoa lately, and it seems that some scripts tend to lag more than others. i have a script that toggles autohide of the dock and it works, the problem is that if i just started the app, this script lags, even spins out before executing:

    Code:
    - (IBAction)showDock:(id)sender
    	{
    	NSString *showDockScript = @"\
    				tell application \"System Events\"\n\
    				tell dock preferences\n\
    				set properties to {autohide:false}\n\
    				end tell\n\
    				end tell";
    	NSDictionary* errorDict;
    	NSAppleEventDescriptor *returnDescriptor = NULL;
    	NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource:showDockScript];
    	returnDescriptor = [scriptObject executeAndReturnError: &errorDict];
    	[NSAppleScript release];
    	}
    
    is there something wrong with the way i wrote it?

    also, is it safe to say that anything applescript can do, cocoa can also do natively without the need to embed applescript?
     
  2. hhas macrumors regular

    Joined:
    Oct 15, 2007
    #2
    It's not particularly efficient, but I can't see anything that'd cause the delays you describe. Have you tried profiling?

    Leopard's Scripting Bridge provides ObjC-Apple Event Manager bindings, although its API is somewhat obfuscated and it's prone to compatibility problems with various applications.

    The other high-level option is objc-appscript (see my sig). e.g. Running your AppleScript command through its ASTranslate tool gives:

    Code:
    #import "SEGlue/SEGlue.h"
    SEApplication *systemEvents = [SEApplication applicationWithName: @"System Events"];
    SEReference *ref = [[systemEvents dockPreferences] properties];
    id result = [ref setItem: [NSDictionary dictionaryWithObject: ASFalse forKey: [SEConstant autohide]]];
    There are also various lower-level APIs if you need them.
     
  3. Sayer macrumors 6502a

    Sayer

    Joined:
    Jan 4, 2002
    Location:
    Austin, TX
    #3
    You could also move the part of code that compiles the script into an object into the startup portion of the app and simply execute it in the IBAction method.

    Also there should be some new APIs in Leopard to hide elements of the screen such as the Dock and/or menu bar for kiosk/full screen purposes.
     
  4. Darkroom thread starter Guest

    Darkroom

    Joined:
    Dec 15, 2006
    Location:
    Montréal, Canada
    #4
    running this code thru instruments i get a memory spike... which i guess is like a memory leak that quickly solves itself? up and down, 32 bytes... never seen that before. the source of the spike is this line:

    Code:
    NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource:showDockScript];
    
    could that have something do do with how sometimes, often when i first launch the app, it takes a second or 2 to execute the applescript?

    what's profiling? :eek:

    previously i've used the carbon framework and SetSystemUIMode(kUIModeAllHidden, 0); to get fullscreen stuff... is that what you mean? or is there something new for leopard?
     
  5. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #5
    You are calling [NSAppleScript release]; but that doesn't make sense. Call [scriptObject release]; instead, or just use autorelease (which I'd suggest getting in the habit of doing, instead of calling release later).
     
  6. Darkroom thread starter Guest

    Darkroom

    Joined:
    Dec 15, 2006
    Location:
    Montréal, Canada
    #6
    woops! missed that one :p... i have to admit that this is not the first time i've done this very thing.

    memory leak is gone, but it still lags at the start.
     
  7. Bakerman macrumors member

    Joined:
    Jan 31, 2005
    Location:
    Sweden
    #7
    In my app I put the compiling of AppleScript snippets into a separate thread that runs at startup (on 10.5, so I can use NSOperation). I found that the user-experience suffers if I compile it just when it is needed, especially on older hardware.
     
  8. Darkroom thread starter Guest

    Darkroom

    Joined:
    Dec 15, 2006
    Location:
    Montréal, Canada
    #8
    so i placed the applescript that hides the dock into an object that initiates at launch, but i still get a lag... could this be on my computer only? here's my code:

    Code:
    - (id)init
    	{
    	if ([super init])
    		{
    		hideDockScriptObject = [[NSAppleScript alloc] initWithSource:[self hideDockScript]];
    		}
    	return self;
    	}
    
    - (NSString *)hideDockScript
    	{
    	return	@"\
    		tell application \"System Events\"\n\
    		tell dock preferences\n\
    		set properties to {autohide:true}\n\
    		end tell\n\
    		end tell";
    	}
    	
    - (IBAction)hideDock:(id)sender
    	{
    	NSDictionary* errorDict = nil;
    	[hideDockScriptObject executeAndReturnError: &errorDict];
    	
    	if (errorDict)
    		{
    		NSLog(@"NSDictionary Error.");
    		}
    	}
    
    is there something there that is tripping the system? it only stalls once, the first time the code is executed... then for the rest of the time while the app is open it's very fast.
     
  9. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #9
    The "lag" is probably from compiling the AppleScript and then executing it, which is what executeAndReturnError does. When you create the object in init, try compiling it there first with compileAndReturnError, so that you don't have to compile it when using it.
     
  10. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #10
    This may be speaking from a place of misunderstanding, but since there is nothing programatically generated in your script, couldn't you just compile it, but the compiled version in your app bundle, and just run the already-compiled copy when needed? Then you never have to compile the script at runtime.

    -Lee
     
  11. Darkroom thread starter Guest

    Darkroom

    Joined:
    Dec 15, 2006
    Location:
    Montréal, Canada
    #11
    it seems instant now, but i can't be really sure because sometimes it's instant if it's still cached somewhere... but i assume it's good because i've followed your advice and compiled it during init. anyway, should this make it faster? is this code correct? have i included something i shouldn't have? i only ask because i don't 100% understand NSAppleEventDescriptor *returnDescriptor.

    Code:
    - (id)init
    	{
    	if ([super init])
    		{
    		NSDictionary* errorDict = nil;
    		showDockScriptObject = [[NSAppleScript alloc] initWithSource:[self showDockScript]];
    		[showDockScriptObject compileAndReturnError:&errorDict];
    		}
    	return self;
    	}
    
    - (NSString *)showDockScript
    	{
    	return	@"\
    		tell application \"System Events\"\n\
    		tell dock preferences\n\
    		set properties to {autohide:false}\n\
    		end tell\n\
    		end tell";
    	}
    	
    - (IBAction)showDock:(id)sender
    	{
    	NSDictionary* errorDict = nil;
    	NSAppleEventDescriptor *returnDescriptor = nil;
    	returnDescriptor = [showDockScriptObject executeAndReturnError:&errorDict];
    	}
    
     
  12. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #12
    It looks fine to me. If you're not using errorDict, just pass nil instead, and for returnDescriptor, just don't assign it. Makes for cleaner and easier to read code.
     

Share This Page