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

上一篇: PyObjc autorelease pool

下一篇: limit on using autorelease pools in ios