这是实际的代码:
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
如果我加载python控制台并仅导入模块,则会因此追溯而失败:
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
为了进行实验,我将池分配移动到每次运行时抛出原始消息的函数(Speak函数)。这样做也会抑制消息,但这次,当应用程序退出时,我得到了:
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 :(得分:0)
我不知道Python或PyObjc,但是Cocoa中充满了各种类,假设在使用它们时始终存在自动释放池。
自动释放池通常在主线程上每次传递事件循环时创建一次,当您创建后台线程时,您需要在自动释放池中为它设置。
所以,你的线程中的第一行代码应该是创建一个自动释放池,最后一行应该告诉池删除它自己以及它收集的所有对象。
另外,如果你的线程中有任何代码段要执行的时间超过1毫秒,那么你应该将该操作包装在自动释放池中。
自动释放池只是临时创建的对象数组,需要尽快删除。它允许您分配内存而不必担心解除分配,因为当池在半毫秒后刷新时,它将为您完成。
无法创建自动释放池不会导致任何崩溃或错误,但它会导致内存泄漏......这很快就会被内核推送到硬盘驱动器作为虚拟内存。如果这只是你正在玩的实验性代码,我不会太担心它。但绝对要在生产代码中修复它。
答案 1 :(得分:0)
在线程中,您应该在run方法的开头创建一个自动释放池并在结束时释放它,但是最好比线程长时间运行时更频繁地清理池(即,当你启动线程然后保持它运行直到程序结束)因为池保持临时对象存活直到它被刷新。
当您有一个队列或其他机制向语音线程发送请求时,您可以执行以下操作:
def run(self):
while True:
request = self.get_work() # fetch from queue, ....
pool = NSAutoreleasePool.alloc().init()
self.use_cocoa_apis()
del pool
答案 2 :(得分: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)
现在我不再收到导入错误,并且引擎没有抛出池错误。我会继续测试,看看我是否收到了之前提到的随机崩溃......