优化 NSRegularExpression 性能

Optimize NSRegularExpression performance

我的应用有一个 feed,其中的帖子可以包含 URL、#hashtags 和 @mentions。我使用名为 ActiveLabel 的广告连播来显示它们。这个 pod 做得很好,但我的提要在滚动时略有滞后。我的提要是 UICollectionView,细胞生成略有滞后。我在滚动时分析了我的应用程序,并分析了滞后峰值。滞后 几乎 不明显,但这让我很烦。

如您所见,主要罪魁祸首是 NSRegularExpression 搜索。

我试图通过在没有数据类型的实例时禁用数据检测来稍微优化这一点,使用 .contains()。这使它稍微快了一点,但滞后尖峰仍然存在。

let enabledTypes:[ActiveType] = {
        var types = [ActiveType]()
        if ad.caption.current.string.contains("#") { types.append(.hashtag) }
        if ad.caption.current.string.contains("@") { types.append(.mention) }
        if ad.caption.current.string.contains("://") { types.append(.url) }
        if ad.caption.canExpand { types.append(seeMore) }
        return types
    }()
label.enabledTypes = enabledTypes

我也遵循了 this article 中的每一步,这有点帮助,但还不够。所以我需要修复正则表达式。

正则表达式语句 ActiveLabel 使用的是

static let hashtagPattern = "(?:^|\s|$)#[\p{L}0-9_]*"
static let mentionPattern = "(?:^|\s|$|[.])@[\p{L}0-9_]*"
static let urlPattern = "(^|[\s.:;?\-\]<\(])" + "((https?://|www\.|pic\.)[-\w;/?:@&=+$\|\_.!~*\|'()\[\]%#,☺]+[\w/#](\(\))?)" + "(?=$|[\s',\|\(\).:;?\-\[\]>\)])"

并将它们与

一起使用
 static func getElements(from text: String, with pattern: String, range: NSRange) -> [NSTextCheckingResult]{
    guard let elementRegex = try? NSRegularExpression(pattern: pattern, options: [.caseInsensitive]) else { return [] }
    return elementRegex.matches(in: text, options: [], range: range)
}

我四处搜索其他正则表达式来检测主题标签和提及,但我没有发现任何有影响的东西。

我试图在后台线程上布置标签,但显然失败了,因为 UI 不喜欢在后台线程上完成。我 可以 重写 ActiveLabel 以主要在我可以使用的后台线程上工作,并使用回调而不是 return 类型,但我想避免这种情况。

我检测到数据的一些字符串样本:

"Arnie says, Aspen. Str. Small. Varm og god jakke. Veldig fin på! Fremstår ubrukt. Kun brukt et par ganger, rett og slett fordi jeg har alt for mange jakker #urban #arnie #says #aspen #ubrukt"

"Skjorte pent brukt i organisk bomull fra tom tailor originalpris 300kr #organisk #bomullsjorte #bomull #flower #floral"

"Jean Paul genser i 100% ull, pent brukt✨ er i str.m, men veldig liten, passer xs-s! \n #jeanpaul #genser #classy #litebrukt #brun #ull"

如您所见,我们的用户主要是标签内容,因此其中一个是最重要的。

有什么方法可以改进 NSRegularExpression 或正则表达式语句以避免性能下降?

您可以尝试在变量(class 或实例)中创建和存储 NSRegularExpression 个实例,这样您就只创建了一次。

,您最好的做法很可能是提前创建一个或多个 NSRegularExpression 实例,并在需要时重用它们。

请注意,由于正则表达式的 creation/compiling 对您的时间配置文件产生了最大的影响(至少,在您共享的时间配置文件中),缓存正则表达式可能会让您赢回足够的性能,您不再需要仅启用所需检测元素的中间优化。在那种情况下,您只需要一个正则表达式(代表检测所有可能的元素类型),因此 caching/reuse 很容易。

此外请注意,您的中间 "optimization" 一开始可能实际上并没有提高性能——它甚至可能会损害性能。匹配正则表达式,无论多么复杂,都需要(大致)完整地搜索整个字符串一次。试图决定要检测的元素类型意味着多次搜索字符串——每个 contains("#") (等)测试一次,然后再一次根据正则表达式评估字符串。重复的字符串搜索可能比编译单个正则表达式的成本更高。

如果您在实施单个缓存通用正则表达式后发现您仍然(以某种方式)在正则表达式性能上受限,您可以缓存多个正则表达式,一个用于您正在处理的每个搜索场景。组合学可能会得出这样的结果,即与要处理的字符串相比,不同的正则表达式仍然要少得多,因此,如果您在用户开始滚动之前就全部编译它们,那么您就不会在滚动期间支付编译它们的时间成本。不过,根据上一段,这只有在您有一种廉价的(即不是字符串搜索)方法来检测每个字符串需要哪个正则表达式时才有意义。