我正在尝试在我的应用程序中处理一个大文本文件。我知道在读取数据时要小心消耗的内存量。一旦读取了一段数据,应用程序就不需要保留这些数据。
感谢“Martin R”和他的帖子Read a file/URL line-by-line,为我启动了工作。
我正在尝试监控我的应用程序在读取大数据文件时的内存消耗,以确保它的行为符合预期。这就是我遇到问题的地方。
当我使用Xcode中的Command-I运行Instruments并监视分配时,我发现在读取文件时,应用程序会达到约15MB的高峰,然后再降回来。这是相当可重复的,误差在+/- 0.5MB之间。
当我使用Xcode中的Command-R运行应用程序,并让其完成对文件的读取,然后在Instruments中按下记录按钮时,内存消耗现在会急剧增加到约360MB。
因此,我进行内存分配测量的两种方式是: Profile: 1. Xcode Command-I. 2. Instruments Record Allocations. 观察到约15MB Simulate and Profile: 1. Xcode Command-R. 2. 让应用程序运行到“IDLE”状态。 3. Instruments Record. 观察到约360MB。
我一直在试图弄清楚几件事情。 Q1. 为什么会有这种差异?(这可能回答了我所有的问题)
Q2. 我是否有真正的问题,或者这只是因为调试代码被注释到模拟器上的原因?
Q3. 类似于Q2,如果我在真实设备上运行调试版本,它会有同样的问题吗?
Q4. 对于我的应用程序,当解析文件时,约15MB是可以接受的,但约360MB则不行。有没有其他方法可以在我的设备上继续进行调试,而不会遇到这个360MB的问题?
版本8.1(8B62) Sierra 2.7Ghz i5 MacBook Pro Circa 2015
附带示例代码。文件的前半部分只是从引用帖子中复制的代码,以方便读者使用。人们可以将此代码按原样在Xcode中运行。在底部是ViewController ViewDidLoad()方法,在那里“运行”事情。内存“膨胀”发生在“File opened”之后。
感谢“Martin R”和他的帖子Read a file/URL line-by-line,为我启动了工作。
我正在尝试监控我的应用程序在读取大数据文件时的内存消耗,以确保它的行为符合预期。这就是我遇到问题的地方。
当我使用Xcode中的Command-I运行Instruments并监视分配时,我发现在读取文件时,应用程序会达到约15MB的高峰,然后再降回来。这是相当可重复的,误差在+/- 0.5MB之间。
当我使用Xcode中的Command-R运行应用程序,并让其完成对文件的读取,然后在Instruments中按下记录按钮时,内存消耗现在会急剧增加到约360MB。
因此,我进行内存分配测量的两种方式是: Profile: 1. Xcode Command-I. 2. Instruments Record Allocations. 观察到约15MB Simulate and Profile: 1. Xcode Command-R. 2. 让应用程序运行到“IDLE”状态。 3. Instruments Record. 观察到约360MB。
我一直在试图弄清楚几件事情。 Q1. 为什么会有这种差异?(这可能回答了我所有的问题)
Q2. 我是否有真正的问题,或者这只是因为调试代码被注释到模拟器上的原因?
Q3. 类似于Q2,如果我在真实设备上运行调试版本,它会有同样的问题吗?
Q4. 对于我的应用程序,当解析文件时,约15MB是可以接受的,但约360MB则不行。有没有其他方法可以在我的设备上继续进行调试,而不会遇到这个360MB的问题?
版本8.1(8B62) Sierra 2.7Ghz i5 MacBook Pro Circa 2015
附带示例代码。文件的前半部分只是从引用帖子中复制的代码,以方便读者使用。人们可以将此代码按原样在Xcode中运行。在底部是ViewController ViewDidLoad()方法,在那里“运行”事情。内存“膨胀”发生在“File opened”之后。
//
//
import UIKit
/* Originally from
* stackoverflow:
* https://dev59.com/n2Af5IYBdhLWcg3wey3Z
* 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.
}
}