2013-05-09 58 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