NSOperationQueue in ASOC

I am trying to use NSOperationQueue in ASOC.

I know how to use NSOperationQueue in Objective-C

Objective-C example:
//create operation queue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

//add operation with block to operation queue
[queue addOperationWithBlock:^{
[NSThread sleepForTimeInterval:300.0];
}];

However, I don’t know how to do it in AppleScriptObjc.
This is the source I thought:

    tell class "NSOperationQueue" of current application
            set queue to NSOperationQueue's alloc()'s init()
            queue's addOperationWithBlock_("delay 300")
        end tell

But this didn’t work…(lldb error?), just as I thought.
Could anyone tell me how to use NSOperationQueue?

I use NSOperation because I want to stop “delay 300” from another method. But I don’t know I can stop it. Is this wrong? Can’t I stop “delay” with NSOperationQueue’s “cancelAllOperations”?

Sorry for my broken English.

Model: MacBook Air
AppleScript: 2.5.1
Browser: Safari 536.30.1
Operating System: Mac OS X (10.8)

You can use NSOperationQueue, but you can’t use blocks – so addOperationWithBlock: is out.

But I don’t follow what you’re trying to do. Your Objective-C code make a new queue and then tells it to do nothing for five minutes – why?

Sorry, this is sample.
What I want to do is to stop “delay” from another method.
My project (not sample) has “delay” and I want to stop the method to press “STOP” button.

Why can’t I use blocks of NSOperationQueue?
We can use blocks in Objective-C.

Shane meant that you can’t use blocks in AppleScriptObjC, not in NSOperationQueue.

You could use performSelectorInBackground:withObject: maybe? Not sure if it applies to your case though, not enough details.

I’m afraid I still don’t understand. The normal reason for using an operation queue like that is so you can do some process on a separate thread without blocking the main thread. If that’s what you’re trying to do, I’m afraid you’re still out of luck. You’re app has only one instance of AppleScript, so splitting AS code onto multiple threads doesn’t gain you anything (and can potentially cost you more).

And yes, I meant you can’t use blocks in ASObjC.

But if you know Objective-C, a hybrid app might be the best solution.

Wow, I didn’t know that.
However, my code has:

my performSelectorInBackground_withObject_("method", AppDelegate)

I complain about my code because it sometimes freezes. I think this freeze occurred because I didn’t create autoreleasepool.
I didn’t do it because I couldn’t do it.

Without ARC, we can do:
set pool to NSAutoreleasePool’s alloc()'s init()

pool’s drain()

But with ARC, we can’t do it. Instead, we use @autoreleasepool in Objective-C.
Objective-C has @autoreleasepool, but I don’t know how to use @autoreleasepool in ASOC.

Doesn’t ASOC have @autoreleasepool?
And can I avoid this freeze with using “NSOperationQueue”?

BTW, I have to stop delay. How can I do?
NSTimer has “invalidate()”.
I tried NSTimer but I couldn’t. NSTimer does not wait for the next line.
For example:

on buttonClicked_(sender)
    log "Button was clicked"
    NSTimer's scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(300, me, "timerFired:", "something", false)
    log "Finished"
end buttonClicked_

on timerFired_(theTimer)
    log "timerFired_ was called"
end timerFired_

Output:
(0s)Button was clicked
(0s)Finished
(300s)timerFired_ was called

This is not what I want to do.
What I want to do is:

on buttonClicked_(sender)
    log "Button was clicked"
    delay 300
    log "Finished"
end buttonClicked_

Output:
(0s)Button was clicked
(300s)Finished

So, I decided not to use NSTimer. But if NSTimer can be used like delay, I will use.
What I want to do is to stop delay or NSTimer or sleepfortimeinterval.

No, you can’t use @autoreleasepool in ASObjC – all memory management is done at the framework level, out of your control.

If you want to block your main thread for 300 seconds, you can use delay, or sleepForTimeInterval:, or sleep, or whatever. But by definition that freezes your application.

The example you say you don’t want to use is the sort of thing you must use if you don’t want your app to freeze.

(And even if you could use an operation queue block, the result would be the same.)

So, is there any way to stop “delay”?
And I think “performSelectorInBackground_withObject_” will make the app leak memory without autoreleasepool.
How do you avoid this?

I’m sorry, but that doesn’t make a lot of sense. You either block the thread, in which case nothing else can happen, or you go on, using a timer or something similar to make the thing you are delaying happen later.

And I think "performSelectorInBackground_withObject_" will make the app leak memory without autoreleasepool.
How do you avoid this?

You either turn on garbage collection for your app, or you use ARC and limit its use to 10.8 and later.

I wrote codes like this:


on startButtonPressed_(sender)
my performSelectorInBackground_withObject_("backgroundMethod", AppDelegate)
end startButtonPressed_

on stopButtonPressed_(sender)
-- I want to stop "backgroundMethod()" here!
end stopButtonPressed_

on backgroundMethod()
...
delay 300
...
end backgroundMethod

I want to stop background thread.

In addition, I turned on ARC, but “performSelectorInBackground_withObject_” needs autoreleasepool.

I’m afraid you can’t. Moreover, your code is pointless because your background thread is still going to block any AppleScript happening on your main thread.

You basically have to consider ASObjC as single-threaded. it’s not quite that simple, but it’s pretty close.

Log a bug. You have no access to memory management as it stands; it’s all meant to be handled by the scripting bridge.

We might get further if you can explain what you want to happen, rather than what you’re trying to code (although sometimes that’s easier to explain).

OK, I want to make auto-clicker.
Once I push “Start” button, my app starts to auto-click.
But I want to stop auto-click with pushing “Stop” button.
Now, I push “Stop” button, my app quits. That’s because I can’t stop the method without quitting my app.

What do you mean by “auto-click”?

One way or another, your method is going to have to periodically give up time to the run loop to check for the pressed button.

You can use something like the doEventFetch() handler below, from my book, or more efficiently use the similar Objective-C method in my Myriad Helpers.

on doEventFetch()
repeat
tell current application's NSApp to set theEvent to nextEventMatchingMask_untilDate_inMode_dequeue_(((current application's NSLeftMouseDownMask) as integer) + ((current application's NSKeyDownMask) as integer), missing value, current application's NSEventTrackingRunLoopMode, true)
if theEvent is missing value then
exit repeat
else
tell current application's NSApp to sendEvent_(theEvent)
end if
end repeat
end doEventFetch

Sorry, my English is wrong.
I wanted to say it like “Automated clicker” that clicks regularly on items in a flash game, in order to play it efficiently.

Thank you for your code. I will try it tomorrow.

What does your method do?
And I don’t know how to use it.

I think performSelectorInBackground_withObject_ is unstable. So I want to use NSOperationQueue without blocks.
But there’s no sample for NSOperationQueue with ASOC. I googled ‘nsoperationqueue applescriptobjc’, but nothing was found.

Could you tell me how to use NSOperationQueue in ASOC?

I’ve never used it in ASObjC, but I imagine it’s the same as Objective-C: you will need to make a subclass of NSOperation.

But I still don’t believe it will provide the magic bullet you think it will. You say performSelectorInBackground_withObject_ is unstable, but I suspect you’re seeing the effects of two threads vying for one instance of AppleScript. Using an operation queue faces exactly the same problem.

You really need to either break your method into a series of short methods, or intersperse it with calls that run the event queue. It’s that, or give up on ASObjC.

I don’t think my app is vying for one instance, because main thread is used only for refreshing UI.

Yesterday, I noticed that my app freezes at performSelectorInBackground_withObject_ when I use a lot of my Mac’s CPU, like video encoding. Is this occurred by leaking memory? Or anything else?

But switching between the two threads also incurs a lot of overhead.

It may well be that you have no spare CPU capacity for the background thread to run on.