|

上篇文章講述了iOS內(nèi)存管理的基本概念,這里是一些內(nèi)存優(yōu)化的小技巧 Strong Weak Dance這個大家都知道,就是處理循環(huán)引用,合理使用weak和unowned。 降低內(nèi)存峰值Lazy Allocation延時加載是很常用的一種優(yōu)化方法,如果有些情況我們不會立即使用某一對象和某些資源,我們完全可以在使用的時候再進(jìn)行加載,這些就可以避免初次運(yùn)行程序的時候內(nèi)存消耗嚴(yán)重。 lazy var goodsImageView: UIImageView = {
let goodsImageView = UIImageView()
return goodsImageView
}()
在喵神的博客中,也發(fā)現(xiàn)了很好玩的地方,Swift標(biāo)準(zhǔn)庫中,定義了一些Lazy方法,可以配合像 map或是 filter 這類接受閉包并進(jìn)行運(yùn)行的方法一起,讓整個行為變成延時進(jìn)行。 let data = 1...3
let result = data.lazy.map {
(i: Int) -> Int in
print('正在處理 \(i)')
return i * 2
}
print('準(zhǔn)備訪問結(jié)果')
for i in result {
print('操作后結(jié)果為 \(i)')
}
print('操作完畢')
運(yùn)行結(jié)果為: // 準(zhǔn)備訪問結(jié)果
// 正在處理 1
// 操作后結(jié)果為 2
// 正在處理 2
// 操作后結(jié)果為 4
// 正在處理 3
// 操作后結(jié)果為 6
// 操作完畢
圖片的讀取相信寫代碼的時候都會涉及到圖片的讀取,我們經(jīng)常使用的方法 imageView?.image = UIImage(named: name)
imageView?.image = UIImage(contentsOfFile: path)
NSData & 內(nèi)存映射文件在我們經(jīng)常使用NSData時,出鏡率很高的兩個方法 public init?(contentsOfFile path: String)
public init(contentsOfFile path: String, options readOptionsMask: NSDataReadingOptions) throws
第二種比第一個多一個Options,第二種方式是創(chuàng)建了一個內(nèi)存映射文件,把內(nèi)容放在虛擬內(nèi)存中,只有讀取操作的時候才會讀到相對應(yīng)頁的物理內(nèi)存頁中。所以后者,對于大文件是很劃算的,推薦使用第二種。對于可選的方式如下: 
其他方式在莊延軍老師的分享中,還有其他一些技巧,但是對于只懂Swift的我,實(shí)在沒接觸過哪些方案,等我接觸之后,再來寫第三個分享。下面簡單列舉一下: calloc VS malloc memset
棧內(nèi)存分配
NSAutoReleasePool從MRC時代說起,當(dāng)需要我們手動來管理對象引用計數(shù)的時候,我們需要Retain和Release方法,那么可能一個線程中有大量的Retain和Release方法,這個時候使用一個池一起管理他們是不是方便很多,將線程中要執(zhí)行的任務(wù)都放在自動釋放池中,自動釋放池會捕獲所有任務(wù)中的對象,在任務(wù)結(jié)束或線程關(guān)閉之時自動釋放這些對象。 當(dāng)然自動釋放池都會使用NSAutoreleasePool類創(chuàng)建,并在自動釋放池收到 drain消息時將這些對象的引用計數(shù)減一,然后將它們從池子中移除 (這一過程形象地稱為“抽干池子”)。 在ARC時代,不再使用NSAutoreleasePool來創(chuàng)建自動釋放池,而是使用@autoreleasepool代碼塊,新建Objective-C工程的時候,會幫我們創(chuàng)建一個main.m文件,已經(jīng)幫我們在主線程創(chuàng)建了一個自動釋放池。 int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
} 更進(jìn)一步,其實(shí)@autoreleasepool 在編譯時會被展開為 NSAutoreleasePool,并且在每個主Runloop結(jié)束時進(jìn)行drain操作。 那么到了Swift時代,又具有了新的變化,不需要手動地調(diào)用autorelease 這樣的方法來管理引用計數(shù),但是這些方法還是都會被調(diào)用的,只不過是編譯器在編譯時在合適的地方幫我們加入了而已。 在Swift標(biāo)準(zhǔn)庫中可以發(fā)現(xiàn): public func autoreleasepool(@noescape code: () -> ())
那么我們就可以利用閉包的特點(diǎn),在需要的時候非常簡便的使用它 autoreleasepool {
// 線程執(zhí)行任務(wù)的邏輯代碼
}
AutoReleasePool為什么在ARC時代還需要使用自動釋放池呢?其原因就是為了避免內(nèi)存峰值,那比如說我有一個很大的For循環(huán),里面不斷讀入較大的文件。其實(shí)每迭代一次,資源都已經(jīng)用完了(就是說我用好了,還你),不需要再用了,這個時候就可以釋放了,但是程序需要等到Runloop結(jié)束的時候才可以釋放,這就增大了內(nèi)存的峰值。 上面的話有點(diǎn)繞,但是真心非常重要。 那么怎么做,可以減小內(nèi)存峰值呢?我們可以在For循環(huán)中添加autoreleasepool,這樣就可以保證每次迭代完畢一次就可以釋放點(diǎn)內(nèi)存。 func loadBigData() {
for i in 1...10000 {
autoreleasepool {
let data = NSData.dataWithContentsOfFile(
path, options: nil, error: nil)
}
}
}
簡單總結(jié)上面的闡述了多種方法,其中對于降低內(nèi)存峰值有作用的是: Lazy Allocation
圖片的讀取的正確方式 NSData & 內(nèi)存映射文件
callocVS malloc memset
棧內(nèi)存分配 autoReleasePool
內(nèi)存警告處理到收到內(nèi)存警告的時候,所要做的:
|