如何通过NSRange截取NSString子串返回NSString而非String类型
NSString 按NSRange截取并返回NSString类型的实现方案
问题现状
默认情况下,Swift环境中调用NSString的substring(with:)方法传入NSRange截取子串时,返回值会被桥接为Swift原生String类型,示例代码如下:
let nsString = NSString("Some string") let nsRange = NSRange(5...10) type(of: nsString.substring(with: nsRange)) // => String.Type
核心诉求
需要实现相同的子串截取逻辑,要求返回值为NSString类型而非String;不接受基于Range<String.Index>的实现方案,要求全程在NSString体系下完成操作以获取更高性能。
性能对比基准
测试代码如下:
let string = String(repeating: "This is it. ", count: 10000) let i1 = 19000 let i2 = 19020 // 耗时统计方法 func tc(computation: (Int, Int) -> Void) { let startTime = DispatchTime.now() for i in 0..<100 { computation(i1 + i, i2 + i) } let endTime = DispatchTime.now() let ns = (endTime.uptimeNanoseconds - startTime.uptimeNanoseconds) print("Time: \(ns)") } // String.Index方案 tc { (s1, s2) in let start = string.index(string.startIndex, offsetBy: s1) let end = string.index(start, offsetBy: s2 - s1) string[start..<end] } // NSRange桥接方案 tc { (s1, s2) in (string as NSString).substring(with: NSRange(s1..<s2)) }
测试结果:
String基于String.Index截取方案耗时:55_394_353nsNSString基于NSRange截取方案耗时:1_389_647ns,性能为前者的39.86倍
实现方法
以下两种实现均全程在NSString/CFString体系下运行,无额外性能损耗:
方法1:显式桥接返回值
Swift导入NSString接口时默认将substring(with:)的返回值转换为Swift String类型,只需将返回结果显式转换为NSString即可。该转换属于零开销桥接,不会触发Swift String的Unicode遍历、索引校准等高成本操作,性能与原生Objective-C调用完全一致:
let nsString = NSString("Some string") let nsRange = NSRange(5...10) let subNSString = nsString.substring(with: nsRange) as NSString type(of: subNSString) // => NSString.Type
方法2:调用CoreFoundation层截取接口
如果需要完全绕开Swift String的桥接逻辑,可直接使用底层CoreFoundation的字符串截取API。CFString与NSString完全支持toll-free桥接,返回结果可直接作为NSString使用,无转换开销:
let nsString = NSString("Some string") let nsRange = NSRange(5...10) let subNSString = CFStringCreateWithSubstring(kCFAllocatorDefault, nsString, nsRange) type(of: subNSString) // => NSString.Type
操作注意:使用过程中请全程持有
NSString类型实例,不要转换为SwiftString类型做后续索引、截取操作,即可维持最优性能。
内容的提问来源于stack exchange,提问作者Peter R
相关产品推荐
相关产品推荐

