3
\$\begingroup\$

I'm trying to get the URL of the browser with Mac OS X app. I wrote some AppleScript and am trying to use it in Cocoa. The problem is, when I watch it with instruments, memory is increasing, and at the end of 3-4 hours it's nearly 20MB.

#import "AppDelegate.h"
#import <Carbon/Carbon.h>

@interface AppDelegate ()
@property (weak) IBOutlet NSWindow *window;
@end

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {

    [NSTimer scheduledTimerWithTimeInterval:1
                                     target:self
                                   selector:@selector(collect)
                                   userInfo:nil
                                    repeats:YES];
}

- (void)collect{
    [self runWithEvent];
}

- (void)runWithEvent{
    NSURL *URL = [[NSBundle mainBundle] URLForResource:@"frontmostapptitle" withExtension:@"scpt"];
    if (URL) {
        NSAppleScript *appleScript = [[NSAppleScript alloc] initWithContentsOfURL:URL error:NULL];

        NSAppleEventDescriptor *returnDescriptor = [self lookUpRunningApp];

        NSDictionary *error = nil;
        NSAppleEventDescriptor *resultEventDescriptor = [appleScript executeAppleEvent:returnDescriptor error:&error];

        if (! resultEventDescriptor) {
            NSLog(@"%s AppleScript run error = %@", __PRETTY_FUNCTION__, error);
        }
        else {
            NSLog(@"%@", [self stringForResultEventDescriptor:resultEventDescriptor]);
        }
    }
}


- (NSAppleEventDescriptor *)lookUpRunningApp{
    // target
    ProcessSerialNumber psn = {0, kCurrentProcess};
    NSAppleEventDescriptor *target = [NSAppleEventDescriptor descriptorWithDescriptorType:typeProcessSerialNumber bytes:&psn length:sizeof(ProcessSerialNumber)];

    // function
    NSAppleEventDescriptor *function = [NSAppleEventDescriptor descriptorWithString:@"LookUpRunningApp"];

    // event
    NSAppleEventDescriptor *event = [NSAppleEventDescriptor
                                     appleEventWithEventClass:kASAppleScriptSuite
                                     eventID:kASSubroutineEvent
                                     targetDescriptor:target
                                     returnID:kAutoGenerateReturnID
                                     transactionID:kAnyTransactionID];
    [event setParamDescriptor:function forKeyword:keyASSubroutineName];
    return event;
}

- (NSString *)stringForResultEventDescriptor:(NSAppleEventDescriptor *)resultEventDescriptor
{
    NSString *result = nil;

    if (resultEventDescriptor) {
        if ([resultEventDescriptor descriptorType] != kAENullEvent) {
            if ([resultEventDescriptor descriptorType] == kTXNUnicodeTextData) {
                result = [resultEventDescriptor stringValue];
            }
        }
    }
    return result;
}

@end
\$\endgroup\$
3
  • \$\begingroup\$ Did you try commenting out lines that don't affect functionality like NSLogs? As the Applescript doesn't change, could you alloc the appleScript once and reuse it? Also the NSAppleEventDescriptor lookUpRunningApp could probably inited once and be reused. In my experience, AppleEvent generally has memory issues. Finally what is your main goal? Could you use NSRunningApplication and CGWindow API? \$\endgroup\$ Feb 12, 2015 at 16:42
  • \$\begingroup\$ What I found more robust is using [[NSAppleScript alloc] initWithSource:@"tell \n -- do work \n end tell" \$\endgroup\$ Feb 12, 2015 at 16:48
  • \$\begingroup\$ For the actual question on memory usage, that feels more appropriate to ask on Stack Overflow, but I'll add that the Instruments app will actually tell you what is consuming your memory, so I recommend checking out what's being allocated and not being deallocated. \$\endgroup\$
    – nhgrif
    Feb 13, 2015 at 0:46

1 Answer 1

2
\$\begingroup\$
- (NSString *)stringForResultEventDescriptor:(NSAppleEventDescriptor *)resultEventDescriptor
{
    NSString *result = nil;

    if (resultEventDescriptor) {
        if ([resultEventDescriptor descriptorType] != kAENullEvent) {
            if ([resultEventDescriptor descriptorType] == kTXNUnicodeTextData) {
                result = [resultEventDescriptor stringValue];
            }
        }
    }
    return result;
}

This method can be rewritten far more simply:

- (NSString *)stringForResultEventDescriptor:(NSAppleEventDescriptor *)resultEventDescriptor {
    return (resultEventDescriptor.descriptorType == kTXNUnicodeTextData) ? resultEventDescriptor.stringValue : nil;
}

- (void)collect{
    [self runWithEvent];
}

This method seems entirely unnecessary. Why doesn't the timer just call runWithEvent directly?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.