简体   繁体   English

Swift - 遍历字符串中的字符会导致内存泄漏

[英]Swift - iterating through characters in string causes memory leak

this is my very first post!这是我的第一篇文章! I'm relatively new to Swift, and don't have a computer science background, so I'm still prone to a lot of newbie problems - but I've been able to solve them all so far, partly from browsing the excellent answers on StackOverflow.我对 Swift 比较陌生,没有计算机科学背景,所以我仍然容易遇到很多新手问题 - 但到目前为止我已经能够解决所有问题,部分原因是浏览了优秀的答案在 StackOverflow 上。 However, I've come across something that's really stumped me, and I've spend a whole week trying to solve it with no luck.然而,我遇到了一些真正让我难堪的事情,我花了整整一周的时间试图解决它,但没有运气。

What I'm trying to do is take text in Chinese from a UITextView and then convert this to an array of individual Chinese characters, which is then used for various processing and analysis.我想要做的是从 UITextView 中获取中文文本,然后将其转换为单个汉字的数组,然后用于各种处理和分析。 However, this causes a leak.但是,这会导致泄漏。

In this greatly simplified example, which reproduces the same leak, there is a TextView and a Button;在这个非常简化的示例中,它重现了相同的泄漏,有一个 TextView 和一个 Button; when the user presses the button, the function makeArray is called, which converts the text to an array of characters (actually Strings of single characters, because I need it to be strings for some of the stuff I will do with it).当用户按下按钮时,函数 makeArray 被调用,它将文本转换为一个字符数组(实际上是单个字符的字符串,因为我需要它是我将用它做的一些事情的字符串)。 The class TextProcessing that contains this function is used as a singleton (yeah, I know that apparently singletons are supposed to be bad, for reasons I don't fully understand, but for various reasons involving other parts of the code it works best when there is a single instance of this class), and the text from the UITextView is passed into it, where it's then converted to the array, as you can see below:包含此函数的类 TextProcessing 用作单例(是的,我知道显然单例应该是坏的,原因我不完全理解,但由于涉及代码其他部分的各种原因,它在是此类的单个实例),并将 UITextView 中的文本传递给它,然后将其转换为数组,如下所示:

class ViewController: UIViewController {
    @IBOutlet weak var textBox: UITextView!
    @IBOutlet weak var doneButton: UIButton!

    @IBAction func pressDoneButton(_ sender: Any) {
        let textToAnalyze = textBox.text!
        TextProcessing.textProcessing.makeArray(textToAnalyze)
    }
}

class TextProcessing {
    static let textProcessing = TextProcessing()

    private let language = "Chinese"
    private var sourceTextArray: [String]!

    func makeArray (_ sourceText: String) {
        if language == "Chinese" {
            sourceTextArray = sourceText.characters.map { String($0) }
        } else if language == "English" {
            sourceTextArray = sourceText.components(separatedBy: " ")
        }
        // then do some stuff with this array
    }
}

When I run this on the Leaks Instruments I get leaks of "Malloc 16 Bytes" and "CFString", with the number of instances of each being roughly the same as the number of array elements (thus the number of characters in the string).当我在 Leaks Instruments 上运行它时,我得到了“Malloc 16 Bytes”和“CFString”的泄漏,每个实例的数量与数组元素的数量大致相同(因此是字符串中的字符数)。 When I look at the Call Tree and drill down, the problem line is "sourceTextArray = sourceText.characters.map { String($0) }".当我查看调用树并向下钻取时,问题行是“sourceTextArray = sourceText.characters.map { String($0) }”。

By the way, this happens with relatively long texts - with short ones, either there's no problem or Instruments doesn't detect it.顺便说一句,这种情况发生在相对较长的文本中 - 对于较短的文本,要么没有问题,要么 Instruments 无法检测到它。

However, if I make an array by separating the string into words according to spaces, like I would want in a language like English, there's no leak - so if I change the language variable to "English" in the example code, it works fine (but of course doesn't give me the array that I want).但是,如果我通过根据空格将字符串分成单词来创建数组,就像我在英语等语言中想要的那样,没有泄漏 - 所以如果我在示例代码中将语言变量更改为“英语”,它可以正常工作(但当然没有给我我想要的数组)。 I thought that maybe the problem was in the "map" method, since it uses a closure and it's easy to have leaks with closures, but when I try other ways of putting it into an array of characters, such as using a for loop and iterating over each character that way, it still has the same problem.我认为问题可能出在“map”方法中,因为它使用了一个闭包,而且闭包很容易泄漏,但是当我尝试其他方法将其放入字符数组时,例如使用 for 循环和以这种方式迭代每个字符,它仍然有同样的问题。

If, instead of getting the text from the UITextView, I do this instead:如果不是从 UITextView 获取文本,而是这样做:

class ViewController: UIViewController {
    @IBOutlet weak var textBox: UITextView!
    @IBOutlet weak var doneButton: UIButton!

    @IBAction func pressDoneButton(_ sender: Any) {
        let textToAnalyze = "blah blah put a long string of Chinese text here"
        TextProcessing.textProcessing.makeArray(textToAnalyze)
    }
}

there's no problem.这里没有问题。 Likewise, if in the makeArray function, if I ignore sourceText and instead do this:同样,如果在 makeArray 函数中,如果我忽略 sourceText 而是这样做:

func makeArray (_ sourceText: String) {
    if language == "Chinese" {
        let anotherText = "blah blah some text here"
        sourceTextArray = anotherText.characters.map { String($0) }
    }
    // then do some stuff with this array
}

there's also no leak.也没有泄漏。 So, something about the combination of getting the string from the text box, passing it into the function, and then putting it into an array of characters is causing the leak.因此,从文本框中获取字符串,将其传递给函数,然后将其放入字符数组的组合会导致泄漏。

I've spent countless hours scouring the internet and reading everything about ARC in Swift, and I've tried all sorts of stuff with weak/unowned etc. and nothing seems to work.我花了无数个小时在互联网上搜索并阅读有关 Swift 中 ARC 的所有内容,并且我尝试了各种弱/无主等内容,但似乎没有任何效果。 What's going on here and how could this be solved?这是怎么回事,如何解决?

Edit:编辑:

So it appears that this might just be a problem with Simulator and/or Instruments.所以看起来这可能只是模拟器和/或仪器的问题。 When I run it on the device, and just monitor memory usage in xcode debug, there's no increase even when doing it 100+ times, so I guess it's OK...it still seems weird that it would show a leak in Instruments though.当我在设备上运行它,并只在 xcode 调试中监视内存使用情况时,即使执行 100 次以上也没有增加,所以我想这没问题……但它会在 Instruments 中显示泄漏似乎仍然很奇怪。

It's instruments bug(there is a lot of issues).这是仪器错误(有很多问题)。 Your code is OK.你的代码没问题。

I just filed a bug report (FB7684067).我刚刚提交了一个错误报告 (FB7684067)。

The following simple macOS command line application will grow to over 1GB in only a few minutes:以下简单的 macOS 命令行应用程序将在几分钟内增长到 1GB 以上:

import Foundation
let line = "124;5678;90123"
while true {
    let fields = line.components(separatedBy: ";")
    assert(fields[1] == "5678")
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM