2

我有一個代碼塊,每20ms執行一次(它是Core Audio音頻單元的渲染回調方法)。這種方法運行一些計算並更新UIImageView的圖像。 爲了迫使UI被刷新,我安排在主線程的UIImageView的圖像的設定器,如下所示:更新UIImageView佔用太多內存

[audioUnit setOutputBlock:^() { 
    // ... 
    // some heavy calculations 
    // ... 
    dispatch_sync(dispatch_get_main_queue(), ^{  
     imageView.image = images[index]; 
    });   
}]; 

沒有圖像的設置器,該應用需要大約50MB內存,而圖像的刷新使其跳到約300MB,導致在iPhone上的內存警告和崩潰< 5.

什麼應該是最好的方法來面對這樣的問題?

非常感謝, DAN

+0

您設置的圖像是什麼?圖像是否已經加載到內存中? – Wain

+0

圖像是110 UIImages的數組,每個圖像分辨率爲1136×640,大小約爲20kb;我已經嘗試加載它們與imageNamed以及imageWithContentsOfFile ..相同的結果。 – DAN

回答

7

你的數據集是相當大的在這裏。在圖像解壓縮之後考慮它:1136高度x 640寬度x每個像素4個字節x 110個圖像= 320MB。不幸的是,圖像解壓縮不是快速/免費的,所以如果你希望這種情況發生在50Hz,你將不得不玩一些技巧。對於初學者來說,您可能無法同時將所有110張圖像保存在內存中,因此請不要試圖這樣做。另外,請不要使用-[UIImage imageNamed:]。它以一種無法控制的方式積極緩存圖像的解壓縮版本。我會使用[[UIImage alloc] initWithContentsOfFile:],以便最大限度地控制圖像的生命週期。

如果解壓縮時間是可以容忍的,那麼它可能就像保留一組圖像路徑一樣簡單,然後在最後一分鐘內製作一個需要的對象。我懷疑20毫秒會有點太慢,每次解壓縮。

另一種選擇是預解壓縮圖像,然後將解壓縮的數據寫入磁盤上應用程序的緩存目錄。這樣,你仍然從磁盤加載,但它是一個直讀,而不是每次讀取和解壓縮。請注意,這將消耗〜320MB的磁盤存儲空間。不是太可怕了。

爲了更進一步,有一些額外的詭計,你可能能夠得到它,因此圖像在磁盤上,然後應用程序中的圖像對象可以指向與每個圖像對應的mmap存儲區域磁盤上的文件。這樣你就可以讓虛擬內存系統決定什麼是重要的內存,什麼不重要,你可以免費獲得。這似乎可能是最好的拍攝。 (編輯:我看了,看起來UIImage已經在後臺映像了,所以這個建議可能對你沒有太大的幫助。)

只是我從你發佈的代碼片段中注意到的一件事:你捕獲整個這意味着陣列中的每個圖像在該塊的持續時間內保持活動(並且一段時間更長,直到libdispatch決定釋放塊)。它可能不會足夠以避免這(因此我的建議如上),但它肯定無助於捕捉整個陣列。這就是我的意思:

[audioUnit setOutputBlock:^() { 
    @autoreleasepool { 

     NSArray* images = [NSArray array]; // or whatever 
     // ... 
     // some heavy calculations 
     // ... 
     UIImage* theOneImageWeNeed = images[index]; 
    } 
    dispatch_sync(dispatch_get_main_queue(), ^{  
     imageView.image = theOneImageWeNeed; 
    });   
}]; 

這將保持你需要的一個圖像活着,並積極釋放由計算生成的任何其他對象。

+1

感謝您的全面回答!我會通讀你的建議,並嘗試改善表演。 只需要注意:圖像數組不是在您編寫的輸出塊內部創建的,而是在相關視圖控制器的viewDidLoad方法中以及在viewDidDisappear中釋放的。 我在哪裏表示「一些繁重的計算」,沒有關於所提到的數組的代碼。 – DAN

+1

那麼你的問題就在那裏。整個過程中,所有圖像都會記憶在您的視野中。嘗試保留一系列路徑並按需創建圖像。 – ipmcc

+0

你對一系列路徑有什麼意義?如果我按需創建圖像,這意味着我必須在「索引」變量的基礎上每隔20ms分配一個新的UIImage,然後釋放前一個;你認爲它會更有效率嗎? – DAN