使用 Swift 对 NSString 数组进行排序导致内存不足警告
Using Swift to sort array of NSString's results in low memory warning
我正在实现一个搜索功能,其中最终结果是 NSString
的 Array
,按它们与搜索字符串的相似程度排序。模糊匹配算法是自定义的,通常不会有问题。
但是,当 Array
包含数千个非常相似的 NSString
(即标题、标题副本、标题 2)时,它确实存在内存问题。 Instruments 报告崩溃时的持久内存是 malloc
的 NSString
的 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 指出的那样,如果 converter
和 scoreAgainst
很昂贵,您可能希望通过为每个项目只调用一次来减少必须执行的调用次数(尽管我会建议您仍然需要 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 }
不清楚 item1
是 String
还是其他什么,所以您需要确保 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)次,并且避免构造临时对象。
我正在实现一个搜索功能,其中最终结果是 NSString
的 Array
,按它们与搜索字符串的相似程度排序。模糊匹配算法是自定义的,通常不会有问题。
但是,当 Array
包含数千个非常相似的 NSString
(即标题、标题副本、标题 2)时,它确实存在内存问题。 Instruments 报告崩溃时的持久内存是 malloc
的 NSString
的 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 指出的那样,如果 converter
和 scoreAgainst
很昂贵,您可能希望通过为每个项目只调用一次来减少必须执行的调用次数(尽管我会建议您仍然需要 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 }
不清楚 item1
是 String
还是其他什么,所以您需要确保 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)次,并且避免构造临时对象。