IdeaMonk

thoughts, ideas, code and other things...

Sunday, October 03, 2010

Stopping it from "just leaking" in PyObjC

*** __NSAutoreleaseNoPool(): Object 0x1040bf0f0 of class NSCFString autoreleased with no pool in place - just leaking


Found a way to get rid of these leakages. We need an NSAutoreleasePool to take care of all actions that happen under threads.

So if you have a CPU/disk/network heavy operation in a module that you're importing into say, a window controller, start them under an NSThread like this -

# foo_module.py
def do_heavy_task((callback, arg1, arg2)):
...
... # all heavy ops here
...
callback(message)


and in the controller -

import foo_module
...
...
class Controller_MainWindow(NSWindowController):
...
...
@IBAction
def btnFoo_Click_(self,sender):
thread = NSThread.detachNewThreadSelector_toTarget_withObject_(
'start_heavy_task',self, (self.return_heavy_task, arg1, arg2) )

def start_heavy_task(self, params):
pool = NSAutoreleasePool.alloc().init()
foo_module.do_heavy_task(params)
del pool

def return_heavy_task(self, msg):
NSLog(msg)
...


Why all the call spaghetti with start_heavy_task, etc?
The issue is with detachNewThreadSelector_toTarget_withObject_ where I haven't been able to figure out how to pass a module's function as selector and the module as object. Also in this way your foo_module can remain independent of cocoa/objc/Foundation pollutions.

Anyhow, this works out into a clean solution. In case you have all your logical code in the window controller itself, you would find this programmish article extremely useful.

Update: a much better way would be to use decorators :D

def AutoPooled(f):
def pooled_func(self):
pool = NSAutoreleasePool.alloc().init()
f(self)
del pool
return pooled_func


Now your start_heavy_task function becomes -

@AutoPooled
def start_heavy_task(self, params):
foo_module.do_heavy_task(params)


How fantastic :D

Labels: , ,

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home