使用 Swift 对 NSString 数组进行排序导致内存不足警告

Using Swift to sort array of NSString's results in low memory warning

我正在实现一个搜索功能,其中最终结果是 NSStringArray,按它们与搜索字符串的相似程度排序。模糊匹配算法是自定义的,通常不会有问题。

但是,当 Array 包含数千个非常相似的 NSString(即标题、标题副本、标题 2)时,它确实存在内存问题。 Instruments 报告崩溃时的持久内存是 mallocNSString 的 98%,我的模糊匹配算法是负责任的调用者。

在不崩溃的较小集合(2,000 个随机字符串)上,内存被释放并且一切都表现出预期的行为。关于如何减少大内存使用的任何想法?

data = data.filter({ (item) -> Bool in
    var itemString = self.converter(item)
    return itemString.scoreAgainst(string) > 0
}).sorted({ (item1, item2) -> Bool in
    var string1 = self.converter(item1)
    var string2 = self.converter(item2)
    return string1.scoreAgainst(string) > string2.scoreAgainst(string)
})

这个方法scoreAgainst真的很干净。它只是进行一系列小写、大写、rangeOfString:substringWithRange: 来给出匹配的分数。

如果内存在完成时被释放,但峰值内存使用量过高,您可以使用 autoreleasepool 来最小化峰值内存使用量:

data = data.filter { (item) -> Bool in
    var isPositive: Bool!
    autoreleasepool {
        let itemString = self.converter(item)
        isPositive = itemString.scoreAgainst(string) > 0
    }
    return isPositive
}.sorted { (item1, item2) -> Bool in
    var isGreaterThan: Bool!
    autoreleasepool {
        let string1 = self.converter(item1)
        let string2 = self.converter(item2)
        isGreaterThan = string1.scoreAgainst(string) > string2.scoreAgainst(string)
    }
    return isGreaterThan
}

正如 Alex 指出的那样,如果 converterscoreAgainst 很昂贵,您可能希望通过为每个项目只调用一次来减少必须执行的调用次数(尽管我会建议您仍然需要 autoreleasepool,因为这种更简单的逻辑减少了对例程的调用次数,但并没有消除它):

data = data.map {
        item -> (String, Double) in
        var score: Double!
        autoreleasepool {
            score = self.converter(item).scoreAgainst(string)
        }
        return (item, score)
    }
    .filter { [=11=].1 > 0 }
    .sorted { [=11=].1 > .1 }
    .map { [=11=].0 }

不清楚 item1String 还是其他什么,所以您需要确保 map 调用的 in 语句匹配它,但希望它能说明这个想法。

你在那里做着非常昂贵和重复的事情;在每次比较调用中构造两个新对象。

原则上,compare 可能被调用多达 N^2 次(对于一个非常糟糕的排序算法),更有可能被调用 N.log(N) 次。

下面的代码怎么样:

data = Array(zip2(data,data.map(self.converter([=10=]).scoreAgainst(string)>0)))
  .filter({ (item,score) -> Bool in
    return score > 0
}).sorted({ (item1, item2) -> Bool in
    return item1.score > item2.score
}).map({(item,score) in item})

这里我们计算分数N次,而不是N + N.log(N)次,并且避免构造临时对象。