Swift 结构扩展添加初始值设定项

标签 swift compiler-errors range

我正在尝试向 Range 添加一个初始化程序。

import Foundation

extension Range {
    init(_ range: NSRange, in string: String) {
        let lower = string.index(string.startIndex, offsetBy: range.location)
        let upper = string.index(string.startIndex, offsetBy: NSMaxRange(range))
        self.init(uncheckedBounds: (lower: lower, upper: upper))
    }
}

但是,最后一行有一个 Swift 编译器错误。

Cannot convert value of type '(lower: String.Index, upper: String.Index)' (aka '(lower: String.CharacterView.Index, upper: String.CharacterView.Index)') to expected argument type '(lower: _, upper: _)'

如何让它编译?

最佳答案

问题是即使String.Index确实符合 Comparable协议(protocol),您仍然需要指定要使用的Range 类型 public struct Range<Bound> where Bound : Comparable {}

注意:如NSString使用 UTF-16,检查 this也在link您已经提到,您的初始代码对于由多个 UTF-16 代码点组成的字符无法正常工作。以下是 Swift 3 的更新工作版本。

 extension Range where Bound == String.Index {
    init(_ range: NSRange, in string: String) {
        let lower16 = string.utf16.index(string.utf16.startIndex, offsetBy: range.location)
        let upper16 = string.utf16.index(string.utf16.startIndex, offsetBy: NSMaxRange(range))

        if let lower = lower16.samePosition(in: string),
            let upper = upper16.samePosition(in: string) {
            self.init(lower..<upper)
        } else {
            fatalError("init(range:in:) could not be implemented")
        }
    }
}

let string = "❄️Let it snow! ☃️"

let range1 = NSRange(location: 0, length: 1)
let r1 = Range<String.Index>(range1, in: string) // ❄️

let range2 = NSRange(location: 1, length: 2)
let r2 = Range<String.Index>(range2, in: string) // fatal error: init(range:in:) could not be implemented

回答 OP 的评论:问题是 NSString 对象对符合 Unicode 的文本字符串进行编码,表示为一系列 UTF–16 代码单元。构成字符串内容的 Unicode 标量值最长可达 21 位。较长的标量值可能需要两个 UInt16 值进行存储。

因此,像❄️这样的字母在 NSString 中占用了两个 UInt16 值,而在 String 中只占用了一个。当您将 NSRange 参数传递给初始化程序时,您可能希望它在 NSString 中正常工作。

在我的示例中,r1 的结果和 r2转换后 string到 utf16 是 '❄️' 和 fatal error 。同时,原始解决方案的结果分别为“❄️L”和“Le”。希望您能看出其中的区别。

如果您坚持不转换为 utf16 的解决方案,您可以查看 the Swift source code做出决定。在 Swift 4 中,您将拥有作为内置库的初始化程序。代码如下。

extension Range where Bound == String.Index {
  public init?(_ range: NSRange, in string: String) {
    let u = string.utf16
    guard range.location != NSNotFound,
      let start = u.index(u.startIndex, offsetBy: range.location, limitedBy: u.endIndex),
      let end = u.index(u.startIndex, offsetBy: range.location + range.length, limitedBy: u.endIndex),
      let lowerBound = String.Index(start, within: string),
      let upperBound = String.Index(end, within: string)
    else { return nil }

    self = lowerBound..<upperBound
  }
}

关于Swift 结构扩展添加初始值设定项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45426830/

相关文章:

ios - Swift - 导入后图像质量下降

Swift 包管理器 - Swift 4 语法

ios - 使用 SKPhysics body 检测碰撞

java - 从Java中的文件读取错误

swift - 代表 RealmSwift 中的范围值?

ios - 如何在执行 segue 之前执行按钮动画

compiler-errors - 在ocaml中将int转换为自然数

c++ - 需要使用 Makefile 编译包含 C++ 和 C 代码的程序

c# - 可能与属性相交的范围列表的更清晰算法

ruby - 如何将范围拆分为 N 个部分