【问题标题】:PyObjc autorelease poolPyObjc 自动释放池
【发布时间】:2013-05-09 01:42:21
【问题描述】:

编辑:感谢您的建议。我仍然不清楚自动释放池的实际处理方式。

这是实际代码:

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

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

经过一番研究,我发现您可以像这样在 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 在对象被销毁时仍在被释放(这是有道理的),但它仍在崩溃。

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

建议?

谢谢!

【问题讨论】:

    标签: nsautoreleasepool pyobjc


    【解决方案1】:

    我不知道 Python 或 PyObjc,但 Cocoa 中有很多类,它们假定在您使用它们时始终存在一个自动释放池。

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

    所以,你的线程中的第一行代码应该是创建一个自动释放池,最后一行应该是告诉池删除自己以及它收集的所有对象。

    另外,如果你的线程中有任何一段代码需要超过 1 毫秒的时间来执行,你也应该将该操作包装在一个自动释放池中。

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

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

    【讨论】:

      【解决方案2】:

      好的,我修改了如下代码:

      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)
      

      现在我不再收到导入错误,引擎也没有抛出池错误。我会继续测试,看看我是否会遇到我之前提到的随机崩溃......

      【讨论】:

        【解决方案3】:

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

        当您有一个队列或其他机制向语音线程发送请求时,您可以执行以下操作:

        def run(self):
        
            while True:
               request = self.get_work() # fetch from queue, ....
        
               pool = NSAutoreleasePool.alloc().init()
               self.use_cocoa_apis()
               del pool
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-04-16
          • 2013-04-18
          • 2014-09-02
          • 2011-08-04
          • 2021-10-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多