PyObjc自动释放池
编辑:感谢您的意见。 我仍然不清楚autorelease池是如何处理的。
这是实际的代码:
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系统崩溃崩溃。
我在这里发现了一些很棒的代码,可以直接与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
该应用程序运行良好,但“只是泄漏”吓坏了我 - 听起来像我正在注视内存泄漏的桶!
在做了一些研究后,我发现你可以像python这样从pyobjc实例化一个自动释放池:
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基础类并不熟悉,所以我不知道如何继续调试。
建议吗?
谢谢!
我不知道Python或PyObjc,但Cocoa充满了假设在使用它们时始终存在自动释放池的类。
通常在主线程上为每个事件循环传递创建一次自动释放池,并且在创建后台线程时,需要在自动释放池中为它设置。
所以,线程中的第一行代码应该是创建一个自动释放池,最后一行应该是告诉池删除它自己以及它收集的所有对象。
另外,如果线程中有任何一段代码需要执行超过1毫秒的时间,那么您应该将该操作封装在自动释放池中。
自动释放池只是临时创建的对象数组,需要尽快删除。 它可以让你分配内存,而不用担心释放内存,因为当池在半毫秒后被刷新时它会被完成。
无法创建自动释放池不会导致任何崩溃或错误,但会造成内存泄漏......这很快会最终被内核作为虚拟内存推送到硬盘驱动器。 如果这只是您正在玩的实验性代码,我不会担心它太多。 但绝对在生产代码中修复它。
在线程中,您应该在run方法开始时创建一个autorelease池,并在最后释放它,但最好是在线程长时间运行时更频繁地清理池(也就是说,当您启动时该线程,然后保持它运行,直到程序结束),因为池保持临时对象直到它被刷新。
当你有一个队列或其他机制向语音线程发送请求时,你可以这样做:
def run(self):
while True:
request = self.get_work() # fetch from queue, ....
pool = NSAutoreleasePool.alloc().init()
self.use_cocoa_apis()
del pool
好的,我修改了这样的代码:
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)
现在我不再收到导入错误,并且引擎不会抛出池错误。 我会继续测试,看看我之前提到的随机崩溃情况是...
链接地址: http://www.djcxy.com/p/72765.html