Swift如何優化性能?

jopen 9年前發布 | 12K 次閱讀 Swift Apple Swift開發

Swift在內存管理上使用的是自動引用計數(ARC)的一套方法,在ARC中雖然不需要手動地調用像是retain,release或者是 autorelease這樣的方法來管理引用計數,但是這些方法還是都會被調用的——只不過是編譯器在編譯時在合適的地方幫我們加入了而已。其中 retain和release都很直接,就是將對象的引用計數加一或者減一。但是autorelease就比較特殊一些,它會將接受該消息的對象放到一個 預先建立的自動釋放池 (auto release pool)中,并在自動釋放池收到drain消息時將這些對象的引用計數減一,然后將它們從池子中移除(這一過程形象地稱為“抽干池子”)。

Swift如何優化性能?

在App中,整個主線程其實是跑在一個自動釋放池里的,并且在每個主Runloop結束時進行drain操作。這是一種必要的延遲釋放的方式,因為我們有時候需要確保在方法內部初始化的生成的對象在被返回后別人還能使用,而不是立即被釋放掉。

在Objective-C中,建立一個自動釋放池的語法很簡單,使用@autoreleasepool就行了。如果你新建一個Objective-C項目,可以看到main.m中就有我們剛才說到的整個項目的autoreleasepool:

    int main(int argc, char *argv[]) {  
        @autoreleasepool {  
            int retVal = UIApplicationMain(  
                argc,   
                argv,   
                nil,   
                NSStringFromClass([AppDelegate class]));  
            return retVal;  
        }  
    }  

更進一步,其實@autoreleasepool在編譯時會被展開為NSAutoreleasePool,并附帶drain方法的調用。

而在Swift項目中,因為有了 @UIApplicationMain ,我們不再需要main文件和main函數,所以原來的整個程序的自動釋放池就不存在了。即使我們使用main.swift來作為程序的入口時,也是不需要自己再添加自動釋放池的。

但是在一種情況下我們還是希望自動釋放,那就是在面對在一個方法作用域中要生成大量的autorelease對象的時候。在Swift 1.0時,我們可以寫這樣的代碼:

    func loadBigData() {  
        if let path = NSBundle.mainBundle()  
            .pathForResource("big", ofType: "jpg") {  
            for i in 1...10000 {  
                let data = NSData.dataWithContentsOfFile(  
                    path, options: nil, error: nil)  
                NSThread.sleepForTimeInterval(0.5)  
            }          
        }  
    }  

dataWithContentsOfFile返回的是autorelease的對象,因為我們一直處在循環中,因此它們將一直沒有機會被釋放。如果數量太多而且數據太大的時候,很容易因為內存不足而崩潰。在Instruments下可以看到內存alloc的情況:

Swift如何優化性能?

這顯然是一幅很不妙的情景。在面對這種情況的時候,正確的處理方法是在其中加入一個自動釋放池,這樣我們就可以在循環進行到某個特定的時候施放內 存,保證不會因為內存不足而導致應用崩潰。在Swift中我們也是能使用autoreleasepool的——雖然語法上略有不同。相比于原來在 Objective-C中的關鍵字,現在它變成了一個接受閉包的方法:

func autoreleasepool(code: () -> ())

利用尾隨閉包的寫法,很容易就能在Swift中加入一個類似的自動釋放池了:

    func loadBigData() {  
        if let path = NSBundle.mainBundle()  
            .pathForResource("big", ofType: "jpg") {  
            for i in 1...10000 {  
                autoreleasepool {  
                    let data = NSData.dataWithContentsOfFile(  
                        path, options: nil, error: nil)  
                    NSThread.sleepForTimeInterval(0.5)  
                }  
            }          
        }  
    }   

這樣改動以后,內存分配就沒有什么憂慮了:

Swift如何優化性能?

這里我們每一次循環都生成了一個自動釋放池,雖然可以保證內存使用達到最小,但是釋放過于頻繁也會帶來潛在的性能憂慮。一個折中的方法是將循環分隔開加入自動釋放池,比如每10次循環對應一次自動釋放,這樣能減少帶來的性能損失。

其實對于這個特定的例子,我們并不一定需要加入自動釋放。在Swift中更提倡的是用初始化方法而不是用像上面那樣的類方法來生成對象,而且在Swift 1.1中,因為加入了可以 返回nil的初始化方法 ,像上面例子中那樣的工廠方法都已經從API中刪除了。今后我們都應該這樣寫:

let data = NSData(contentsOfFile: path)

使用初始化方法的話,我們就不需要面臨自動釋放的問題了,每次在超過作用域后,自動內存管理都將為我們處理好內存相關的事情。

作者:王巍(@onevcat),iOS和Unity3D開發者。

本文轉載自: Swifter

點擊鏈接進入Swift技術社區,了解更多Swift開發的技術熱點內容!

原文  http://www.csdn.net/article/2015-03-04/2824102-swift-autoreleasepool

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!