2016-11-13 27 views
2

我想在我的應用程序中處理一個大文本文件。我知道我在讀取數據時要小心所消耗的內存量。一旦讀取了一段數據,應用程序就不需要保存數據。ios iPhone模擬器是否會導致內存使用率分析增大?

感謝「馬丁R」和職位Read a file/URL line-by-line幫助我開始我的努力。

我試圖監視我的應用程序的內存消耗,因爲它讀取大型數據文件,以便我可以確定它的行爲如預期。這是我遇到問題的地方。

當我從Xcode內部使用Command-I運行儀器並監視分配時,我發現在讀取文件的過程中,應用程序在〜15MB時查看,然後再次下降。這是相當可重複的+/- 0.5MB。

當我使用Command-R從Xcode中運行應用程序,然後讓它完成對文件的讀取,然後按下樂器中的記錄時,內存消耗現在膨脹到〜360MB。

所以要澄清,這兩種方式我已經做了內存分配的測量是:
簡介:
1的Xcode命令我。
2.儀器記錄分配。觀察〜15MB
模擬和配置文件:
1. Xcode Command-R。
2.讓app運行到「IDLE」。
3.儀器記錄。觀察〜360MB。

我一直在試圖弄清楚這裏的一些事情。第一季度。爲什麼區別? (這可以回答我所有的問題)

Q2。我是否有真正的問題,或者這只是一個問題,因爲調試代碼是如何註釋到模擬器的?

Q3。與Q2類似,如果我在真實設備上運行調試版本,它是否會有相同的問題?

Q4。對於我的應用程序,解析文件時可以接受〜15MB,但〜360MB不會。有沒有另一種方式,我可以繼續在我的設備上進行調試,而不需要這360MB的打擊?

8.1版(8B62)
塞拉利昂
2.7GHz的酷睿i5
的MacBook Pro協卡2015年

示例代碼連接。爲了方便讀者,文件的第一部分僅僅是參考文章的代碼的副本。可以按照原樣使用這些代碼並在Xcode中運行它。底部是ViewController的ViewDidLoad()方法,其中的事情「運行」。內存「膨脹」在「文件打開」之後。

// 
// 

import UIKit 

/* Originally from 
* stackoverflow: 
* https://stackoverflow.com/questions/24581517/read-a-file-url-line-by-line-in-swift 
* posted by Martin R. 
* Much thanks! 
*/ 
class StreamReader { 

    let encoding : String.Encoding 
    let chunkSize : Int 
    var fileHandle : FileHandle! 
    let delimData : Data 
    var buffer : Data 
    var atEof : Bool 

    init?(path: String, delimiter: String = "\n", encoding: String.Encoding = .utf8, 
     chunkSize: Int = 4096) { 

    guard let fileHandle = FileHandle(forReadingAtPath: path), 
     let delimData = delimiter.data(using: encoding) else { 
     return nil 
    } 
    self.encoding = encoding 
    self.chunkSize = chunkSize 
    self.fileHandle = fileHandle 
    self.delimData = delimData 
    self.buffer = Data(capacity: chunkSize) 
    self.atEof = false 
    } 

    deinit { 
    self.close() 
    } 

    /// Return next line, or nil on EOF. 
    func nextLine() -> String? { 
    precondition(fileHandle != nil, "Attempt to read from closed file") 

    // Read data chunks from file until a line delimiter is found: 
    while !atEof { 
     if let range = buffer.range(of: delimData) { 
     // Convert complete line (excluding the delimiter) to a string: 
     let line = String(data: buffer.subdata(in: 0..<range.lowerBound), encoding: encoding) 
     // Remove line (and the delimiter) from the buffer: 
     buffer.removeSubrange(0..<range.upperBound) 
     return line 
     } 
     let tmpData = fileHandle.readData(ofLength: chunkSize) 
     if tmpData.count > 0 { 
     buffer.append(tmpData) 
     } else { 
     // EOF or read error. 
     atEof = true 
     if buffer.count > 0 { 
      // Buffer contains last line in file (not terminated by delimiter). 
      let line = String(data: buffer as Data, encoding: encoding) 
      buffer.count = 0 
      return line 
     } 
     } 
    } 
    return nil 
    } 

    /// Start reading from the beginning of file. 
    func rewind() -> Void { 
    fileHandle.seek(toFileOffset: 0) 
    buffer.count = 0 
    atEof = false 
    } 

    /// Close the underlying file. No reading must be done after calling this method. 
    func close() -> Void { 
    fileHandle?.closeFile() 
    fileHandle = nil 
    } 
} 

extension StreamReader : Sequence { 
    func makeIterator() -> AnyIterator<String> { 
    return AnyIterator { 
     return self.nextLine() 
    } 
    } 
} 



class ViewController: UIViewController { 

    override func viewDidLoad() { 
    super.viewDidLoad() 
    // Do any additional setup after loading the view, typically from a nib. 

    let path2WordList = Bundle.main.path(forResource: "large_text_file", ofType: "txt") 
    var wordCnt: Int = 0 

    if nil != path2WordList { 
     if let aStreamReader = StreamReader(path: path2WordList!) { 
     defer { aStreamReader.close() } 
     print("File openned") 

     /* Read and discard */ 
     while aStreamReader.nextLine() != nil { 
      wordCnt += 1 
     } 

     } // if let ... 
    } // if nil ... 

    print ("Final wordCnt := \(wordCnt)") 
    } // viewDidLoad 


    override func didReceiveMemoryWarning() { 
    super.didReceiveMemoryWarning() 
    // Dispose of any resources that can be recreated. 
    } 


} 
+0

作爲一個便箋,無論何時您想使用儀器進行測試,請嘗試使用**真實設備**,因爲macbook pro的模擬器可能比真實設備更優越。請參閱[本視頻](https://www.youtube.com/watch?v=cR4Wc4JGOMg&index=27&list=WL) – Honey

+0

「作爲便箋,只要您想使用樂器進行測試,請嘗試使用真實設備,因爲macbook pro的模擬器可能會比真實設備更優秀。「或更糟。將其視爲佈局的便利工具,而不是更多。 – Confused

回答

1

我一直在使用長期運行while循環時,遇到了這樣的問題。問題是分配到當前autorelease池中的任何東西都不會被釋放,直到循環退出。

爲了防止發生這種情況,您可以在autoreleasepool(invoking:)中包裝while循環的內容。這會導致循環的每次迭代都有自己的autorelease池,每次都會耗盡。

這將是這個樣子:

/// Return next line, or nil on EOF. 
func nextLine() -> String? { 
    precondition(fileHandle != nil, "Attempt to read from closed file") 

    var result: String? = nil 

    // Read data chunks from file until a line delimiter is found: 
    while !atEof, result == nil { 
    result = autoreleasepool { 
     if let range = buffer.range(of: delimData) { 
     // Convert complete line (excluding the delimiter) to a string: 
     let line = String(data: buffer.subdata(in: 0..<range.lowerBound), encoding: encoding) 
     // Remove line (and the delimiter) from the buffer: 
     buffer.removeSubrange(0..<range.upperBound) 
     return line 
     } 
     let tmpData = fileHandle.readData(ofLength: chunkSize) 
     if tmpData.count > 0 { 
     buffer.append(tmpData) 
     } else { 
     // EOF or read error. 
     atEof = true 
     if buffer.count > 0 { 
      // Buffer contains last line in file (not terminated by delimiter). 
      let line = String(data: buffer as Data, encoding: encoding) 
      buffer.count = 0 
      return line 
     } 
     } 
     return nil 
    } 
    } 
    return result 
} 

至於你的記憶增長是否是調試環境的副作用,這是很難說。但無論如何,防止這種增長可能是明智的。

相關問題