【问题标题】:Optimize NSRegularExpression performance优化 NSRegularExpression 性能
【发布时间】:2017-06-02 00:33:41
【问题描述】:

我的应用有一个供稿,其中的帖子可以包含 URL、#hashtags 和 @mentions。我使用名为 ActiveLabel 的 pod 显示它们。这个 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 以主要在我可以使用回调而不是返回类型的后台线程上工作,但我想避免这种情况。

我检测到数据的一些字符串示例:

“Arnie 说,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 #说#aspen #ubrukt”

“Skjorte pent brukt iorganisk bomull fra tom 裁缝 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 或正则表达式以避免性能下降?

【问题讨论】:

  • 您的urlPattern 模式不是最佳的。匹配与模式不匹配的长字符串时可能会导致速度变慢。此外,"(?:^|\\s|$)#[\\p{L}0-9_]*" 将匹配类似# 的字符串。我认为您可以使用static let hashtagPattern = "\\B#[\\p{L}0-9_]+"static let mentionPattern = "\\B@[\\p{L}0-9_]+"
  • 改用NSDataDetector会有帮助吗?
  • @WiktorStribiżew ActiveLabel 框架在另一个位置过滤长度,但我会用你的表达式来测试。

标签: ios objective-c regex swift nsregularexpression


【解决方案1】:

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

请注意,由于正则表达式的创建/编译对您的时间配置文件产生了最大的影响(至少,在您共享的尽可能多的时间配置文件中),缓存正则表达式可能会为您赢得足够的性能,您不再需要只启用您需要的检测元素的中间优化。在这种情况下,您只需要一个正则表达式(表示检测所有可能的元素类型),因此缓存/重用很容易。

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

如果您在实现单个缓存的通用正则表达式后发现您仍然(不知何故)在正则表达式性能方面受到限制,您可以缓存多个正则表达式,一个用于您正在处理的每个搜索场景。据推测,组合学的结果是,您仍然拥有比要处理的字符串少得多的不同正则表达式,因此如果您在用户开始滚动之前将它们全部编译,则您无需支付在滚动期间编译它们的时间成本。但是,根据上一段,这只有在您有一种廉价(即不是字符串搜索)的方式来检测每个字符串所需的正则表达式时才有意义。

【讨论】:

  • 你和@raidfive 的答案是正确的。当你付出更多努力时,我会接受你的。 .contains 确实是减速了。我还制作了一个静态 NSRegularExpression 实例,这很有帮助。事实证明,它的分配是昂贵的。调整 ActiveLabel 以在后台线程上处理回调并没有改善它,所以我恢复了这个更改。我仍然得到微小的延迟峰值,但它现在来自 AlamofireImage 和 SwiftyJSON.. Yelp.
【解决方案2】:

您可以尝试创建 NSRegularExpression 实例并将其存储在变量(类或实例)中,这样您只需创建一次。

【讨论】:

  • NSRegularExpression 需要设置为已初始化,并且在初始化后不允许配置:/
  • 初始化后需要配置什么?查看您的代码示例,您似乎只需要三个 NSRegularExpression 实例及其相关模式。示例:static let hashtagRegex = NSRegularExpression(pattern: "(?:^|\\s|$)#[\\p{L}0-9_]*", options: [.caseInsensitive])
  • 好点,我没想到。在我的脑海中,我认为我需要用我想要匹配的字符串来初始化它。
  • 您可能还在浪费性能试图决定您需要哪个正则表达式,因此您可能只需要缓存一个正则表达式。 (注意my answer——很抱歉偷了你的雷@raidfive,但评论太多了。)
猜你喜欢
  • 2013-03-17
  • 2016-05-14
  • 2011-10-17
  • 2014-01-08
  • 1970-01-01
  • 2013-03-31
  • 2018-07-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多