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

NSTextView绘制前移除自定义属性,应重写哪个类的对应方法?

问题描述

在WWDC讲座《TextKit最佳实践》中提到,自定义属性会拖慢NSTextView的绘制速度,建议在将字符串传入渲染前剥离这些属性。我目前使用的自定义属性仅存储标签数据,完全不会参与渲染流程。
NSTextStorage提供了属性修复方法,但我不想直接从源数据中完全移除这些自定义属性。属性修复相关的文档描述比较模糊,看起来也不适用于我的场景。
请问我应该重写NSTextView、NSTextStorage还是NSLayoutManager中的哪个方法,来控制最终传入绘制环节的NSAttributedString内容?
我已经尽可能查阅了官方文档,也可以正常实现自定义字形等功能,但暂未理清该问题的实现路径,该知识点在WWDC主题演讲中只是快速带过,描述得非常简单。

解决方案

你应该选择**子类化NSTextStorage**来实现需求,这是性能最优、也最符合TextKit架构设计的方案,完全不需要修改源数据里的自定义属性,也不会影响渲染性能。

实现逻辑

WWDC2018提到的自定义属性拖慢绘制的核心原因,是TextKit在布局、绘制阶段遇到未知的自定义属性时,会进行额外的遍历校验。你只需要让布局、渲染链路读取不到这些非渲染用的自定义属性即可,不需要从源存储中删除。
自定义NSTextStorage子类的具体实现步骤:

  • 子类内部持有一个私有的NSMutableAttributedString实例作为完整源存储,所有的数据写入、你自己需要读取自定义标签属性的操作,都直接操作这个源实例
  • 实现NSTextStorage子类要求的四个必须重写的核心方法:
    • string:直接返回源存储的纯文本内容
    • attributesAtIndex:effectiveRange::从源存储拿到对应位置的完整属性后,过滤掉你自定义的非渲染属性key,再把过滤后的属性字典返回,有效范围直接沿用源存储的返回值
    • replaceCharactersInRange:withString::直接调用源存储的同名方法修改内容,完成后调用edited:range:changeInLength:通知上层布局管理器更新
    • setAttributes:range::直接调用源存储的同名方法修改属性,完成后同样调用edited:range:changeInLength:发通知
      这个方案下,上层的NSLayoutManager和NSTextView全程读取不到你的自定义属性,完全不会产生额外的性能开销,而你需要读写标签数据时直接访问内部源存储即可,完全不受影响。

其他方案说明

  • 不推荐重写NSTextView或NSLayoutManager:这两个类的修改只能干预绘制阶段,布局阶段依然会读取到自定义属性,还是会产生额外的性能损耗,实现复杂度也更高
  • NSTextStorage自带的fixAttributesInRange:方法会直接修改源存储的属性内容,不符合你保留源数据自定义属性的需求,不适用于当前场景

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 01:39:04