2013-05-09 66 views
3

编辑:谢谢你的建议。我仍然不清楚autorelease池是如何处理的。PyObjc autorelease pool

下面是实际的代码:

import platform, time 

if (platform.system().lower() == "darwin"): 
    from AppKit import NSSpeechSynthesizer 
    from Foundation import NSAutoreleasePool 

[class's init function] 
def __init__(self): 
    if (platform.system().lower() != "darwin"): 
     raise NotImplementedError("Mac OS X Speech not available on this platform.") 
    self.ve = NSSpeechSynthesizer.alloc().init() 

[function that throws the errors normally] 
def say(self,text,waitForFinish=False): 
    pool = NSAutoreleasePool.alloc().init() 
    self.ve.startSpeakingString_(text) 
    if (waitForFinish == True): 
     while (self.ve.isSpeaking() == True): 
      time.sleep(0.1) 
    del pool 

如果我加载了蟒蛇控制台,仅仅导入模块,它失败与此回溯:

Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "audio/__init__.py", line 5, in <module> 
    from speech_mac import * 
    File "audio/speech_mac.py", line 19, in <module> 
    class SpeechSynthesizer(object): 
    File "audio/speech_mac.py", line 56, in SpeechSynthesizer 
    del pool 
NameError: name 'pool' is not defined 

看起来不知何故Python是不保留了解“池”变量。我真的很困惑,所有这些工作如何 - 正如我在原文中所说的,我不熟悉ObjC或OS X框架。

我阅读了关于使用NSAutoreleasePools的Apple文档,听起来我应该完全按照你们的建议 - 创建池,运行通常似乎抛出异常的代码,然后销毁池。然而,正如你所看到的,这并不像人们期望的那样工作。

如果我离开del pool那么代码会运行并且错误被抑制,但是,如原文所述,在不可预知的情况下,当应用程序实际正在退出时,它会与OS X系统崩溃崩溃原来的帖子。


我在SO上发现了一些很棒的代码,可以直接与Mac OS X的语音合成器引擎接口。它基本上导入AppKit,实例化NSSpeechSynthesizer,然后将它的方法和东西传递给Python。很棒。

我将代码包装到类中以方便使用。

唯一的问题是,在我的应用程序中,语音在单独的线程上运行,因为它连接到一个wxPython应用程序。

在我的控制台,为的是,我得到这样每次什么消息的洪水是口语:

objc[53229]: Object 0x53d2d30 of class OC_PythonString autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug 

该应用程序运行正常,但“只是泄漏”让我害怕 - 听起来像我盯着降低内存泄漏的桶!

做了一些研究之后,我发现你可以从pyobjc在Python中实例自动释放池是这样的:

from Foundation import NSAutoreleasePool 

def __init__(self): 
    self.pool = NSAutoreleasePool.alloc().init() 

def __del__(self): 
    self.pool.release() 

这样做停止出现,但是,它是完全无计划的错误信息,现在在应用程序退出时,我有时会遇到足以导致OS X崩溃对话框崩溃的崩溃。控制台吐出来的是以下情况:

objc[71970]: autorelease pool page 0x4331000 corrupted 
    magic 0xf0000000 0xf0000000 0x454c4552 0x21455341 
    pthread 0xb0811000 

实验,我把池分配到只要它运行(在朗读功能)被抛出原始邮件的功能。这样做,也抑制了消息,但是这一次,当应用程序退出后,我得到了:

Bus error: 10 

与崩溃对话框,弹出如下:

Exception Type: EXC_BAD_ACCESS (SIGBUS) 
Exception Codes: KERN_PROTECTION_FAILURE at 0x0000000000000010 

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread 
0 libobjc.A.dylib     0x926ec465 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 525 
1 com.apple.CoreFoundation  0x99cc3a73 _CFAutoreleasePoolPop + 51 
2 com.apple.Foundation   0x90fca116 -[NSAutoreleasePool release] + 125 

貌似AutoreleasePools仍当物体被破坏(这是有道理的)被释放,但它仍然崩溃。

我对OS X中的Objective C或NS基础类并不熟悉,所以我不确定如何继续调试它。

建议?

谢谢!

回答

0

好吧,我修改这样的代码:

def say(self,text,waitForFinish=False): 
    pool = NSAutoreleasePool.alloc().init() 
    self.ve.startSpeakingString_(text) 
    del pool 
    if (waitForFinish == True): 
     while (self.ve.isSpeaking() == True): 
      time.sleep(0.1) 

现在我不再会导致导入错误,并且引擎不会抛出池错误。我会继续测试,看看我是否得到我之前提到的随机崩溃...

0

我不知道Python或PyObjc,但Cocoa充满了假设在您使用它们时始终存在autorelease池的类。

对于主线程上的事件循环的每次传递,通常会创建一次自动释放池,创建后台线程时,需要在自动释放池中为其设置。

因此,线程中的第一行代码应该是创建一个自动释放池,最后一行应该是告诉池删除它自己以及它收集的所有对象。另外,如果你的线程中有任何代码段需要超过1毫秒的执行时间,你应该把这个操作也包装在一个自动释放池中。

自动释放池只是一个临时创建的对象数组,需要尽快删除。它可以让你分配内存,而不用担心释放内存,因为当池在半毫秒后被刷新时它会被完成。

未能创建自动释放池不会导致任何崩溃或错误,但它会导致内存泄漏......这将很快被内核推出到硬盘上作为虚拟内存。如果这只是您正在玩的实验性代码,我不会担心它太多。但绝对在生产代码中修复它。

0

在线程中,您应该在运行方法开始时创建一个自动释放池并在最后释放它,但最好是在线程长时间运行时更频繁地清理池(即,当你启动线程,然后保持它运行,直到程序结束),因为池保持临时对象直到它被刷新。

当你有一个队列或其他机制将请求发送到语音线程,你可以这样做:

def run(self): 

    while True: 
     request = self.get_work() # fetch from queue, .... 

     pool = NSAutoreleasePool.alloc().init() 
     self.use_cocoa_apis() 
     del pool 
+0

更新的原始问题与尝试此结果... – fdmillion 2013-05-16 08:06:38