You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中AttributedString扩展NSRange下标赋值提示下标只读

问题描述

以下代码可以正常运行:

let nsRange = NSRange(location: 5, length: 10)
var attributedString = AttributedString("This is some string that I want to attribute.")
// 给子串设置属性前,需要先完成范围类型转换
let start = attributedString.index(attributedString.startIndex, offsetByUnicodeScalars: nsRange.location)
let end = attributedString.index(start, offsetByUnicodeScalars: nsRange.length)
// 得到Range<AttributedString.Index>类型的合法范围
let range = start..<end
attributedString[range].foregroundColor = .red

执行无异常。

当尝试编写如下扩展,希望通过自定义下标省略重复的范围转换逻辑时:

extension AttributedString {
  subscript(_ range: NSRange) -> AttributedSubstring {
    get {
      let start = index(startIndex, offsetByUnicodeScalars: range.location)
      let end = index(start, offsetByUnicodeScalars: range.length)
      return self[start..<end]
    }
  }
}

使用如下代码调用:

attributedString[nsRange].foregroundColor = .red

编译失败,抛出错误:Cannot assign through subscript: subscript is get-only。

核心疑问:attributedString[nsRange]返回值为AttributedSubstring类型,该类型本身支持通过.foregroundColor = .red语法设置属性;使用原生Range<AttributedString.Index>下标的语法str[start..<end].foregroundColor = .red可正常工作,为什么自定义NSRange下标无法实现相同效果?


根本原因

问题本质是Swift普通只读下标与集合类型突变下标的行为差异:

  • AttributedString原生接收Range<AttributedString.Index>参数的下标,是MutableCollection协议提供的突变(mutating)下标,调用时会拿到原字符串对应存储段的可写引用,对返回的AttributedSubstring做属性修改,会直接通过引用写回原AttributedString。
  • 自定义的只读下标本质是普通的实例方法,非mutating的getter只会返回一个临时生成的AttributedSubstring值拷贝,这个临时值是生命周期仅存在于当前调用语句的右值,本身不可持久修改。哪怕AttributedSubstring自身支持属性修改,修改的也是会被立即释放的临时对象,完全不会作用到原字符串上,编译器会直接拦截这种无意义的写操作。

报错信息提示"subscript is get-only",就是明确说明当前下标只实现了读取逻辑,没有实现修改后写回原实例的逻辑,无法支持通过下标修改原实例内容。


修复方法

给自定义下标补充set实现,在setter中把修改后的子串写回原字符串的对应范围即可:

extension AttributedString {
    subscript(_ range: NSRange) -> AttributedSubstring {
        get {
            let start = index(startIndex, offsetByUnicodeScalars: range.location)
            let end = index(start, offsetByUnicodeScalars: range.length)
            return self[start..<end]
        }
        set {
            let start = index(startIndex, offsetByUnicodeScalars: range.location)
            let end = index(start, offsetByUnicodeScalars: range.length)
            self[start..<end] = newValue
        }
    }
}

补充setter后,attributedString[nsRange].foregroundColor = .red的调用流程就和原生下标一致:先调用getter拿到对应范围的子串,修改子串属性后,再调用setter将修改后的子串写回原字符串对应位置,可正常编译运行。


内容的提问来源于stack exchange,提问作者Peter R

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 19:09:23