【问题标题】:iOS: UITextView with custom NSTextStorage crashesiOS:带有自定义 NSTextStorage 的 UITextView 崩溃
【发布时间】:2016-04-15 01:35:58
【问题描述】:

我正在用 Swift 编写一个 iOS 9 应用程序。我有一个托管 UITextView 的视图控制器,我正在为其分配一个自定义 NSTextStorage 对象。 NSTextStorage 现在只是基本的。一切正常,文本显示在 UITextView 中,但是当我点击文本进行编辑时,应用程序崩溃并出现以下异常:

2016-01-10 11:24:32.931 PagesWriter[23750:6939530] requesting caretRectForPosition: with a position beyond the NSTextStorage (529)
2016-01-10 11:24:33.040 PagesWriter[23750:6939530] requesting caretRectForPosition: with a position beyond the NSTextStorage (529)
2016-01-10 11:24:33.168 PagesWriter[23750:6939530] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSCFString _getBlockStart:end:contentsEnd:forRange:stopAtLineSeparators:]: Range {529, 0} out of bounds; string length 245'
*** First throw call stack:
(0x181ea5900 0x181513f80 0x181ea5848 0x18278a02c 0x1827e963c 0x186cc6124 0x186b1c818 0x186cc5fbc 0x186d59e68 0x186bdeb60 0x186bde454 0x186fac1f8 0x187501f64 0x186bc2e84 0x186bc4f40 0x186fa65ac 0x186faea5c 0x186bc5c18 0x186bbf1f8 0x186bbed2c 0x186c2047c 0x186c20828 0x186d5811c 0x186d57f94 0x186d57448 0x187118dbc 0x186d3c5b8 0x186bca9b0 0x18711a3bc 0x186b89b58 0x186b868dc 0x186bc8820 0x186bc7e1c 0x186b984cc 0x186b96794 0x181e5cefc 0x181e5c990 0x181e5a690 0x181d89680 0x183298088 0x186c00d90 0x1000615ec 0x18192a8b8)
libc++abi.dylib: terminating with uncaught exception of type NSException

如果我从 UITextView 中删除我的自定义 NSTextStorage 对象,它会起作用。

我的视图控制器使用情节提要中的模态转场加载。我加载文本内容并在 viewDidLoad 方法中配置 UITextView 和 NSTextStorage 对象:

class TextEditorViewController: UIViewController {
  @IBOutlet weak var textView: UITextView!

  // Set by parent view controller during transition
  var URL: NSURL?

  var textStorage: NSTextStorage?

  override func viewDidLoad() {
    super.viewDidLoad()

    textStorage = CustomTextStorage()
    textView.textStorage.removeLayoutManager(textView.layoutManager)
    textStorage!.addLayoutManager(textView.layoutManager)
    dispatch_async(UtilityQueue) {
      do {
        let text = try NSString(contentsOfURL: self.URL!, encoding: NSUTF8StringEncoding)
        dispatch_async(MainQueue) {
          self.textStorage!.replaceCharactersInRange(NSRange(location: 0, length: 0), withString: text as String)
        }
      } catch { /* handle error */ }
    }
  }
}

UtilityQueueMainQueue 是辅助内容,包含对相应调度队列的引用。

我的自定义 NSTextStorage 类如下所示:

class CustomTextStorage: NSTextStorage {
  let backingStore = NSMutableAttributedString()

  override var string: String {
    return backingStore.string
  }

  override func attributedAtIndex(location: Int, effectiveRange range: NSRangePointer) -> [String: AnyObject] {
    return backingStore.attributesAtIndex(location, effectiveRange: range)
  }

  override func replaceCharactersInRange(range: NSRange, withString str: String) {
    backingStore.replaceCharactersInRange(range, withString: str)
    edited(.EditedCharacters, range: range, changeInLength: str.characters.count - range.length)
  }

  override func setAttributes(attires: [String: AnyObject]?, range: NSRange) {
    backingStore.setAttributes(attires, range: range)
    edited(.EditedAttributes, range: range, changeInLength: 0)
  }

  override func processEditing() {
    /* Placeholder; will be adding code here in the future. */
    super.processEditing()
  }
}

有什么想法吗?提前感谢您的帮助。

【问题讨论】:

  • 我在 Mac 应用程序中看到了一个非常相似的错误。我有一种预感,这与 Swift 有关。你找到根本原因了吗?
  • 我已经确认用 swift 编写的 NSTextStorage 子类会显示这个问题。 Objective-C 中的相同实现可以完美运行。我在bugreport.apple.com 向苹果提交了雷达,您也应该这样做。
  • 感谢@ChristianNiles。我没有找到解决办法。我只是将创建和初始化 UITextView 移动到代码中并将其从情节提要场景中取出。
  • 你找到解决办法了吗?
  • @bittu 我遇到了类似的问题,原来是因为我没有在我的NSTextStorage 子类中覆盖addLayoutManager()removeLayoutManager()layoutManagers

标签: ios uitextview nstextstorage


【解决方案1】:

我不知道它是否仍然相关,但你几乎是对的。

您需要分别致电beginEditing()endEditing()

在编辑回调的长度计算中将字符串转换为NSString(否则表情符号和其他一些情况将无法正常工作)。

由于性能问题,请使用NSTextStorage 作为 backingStore。

你会得到这样的东西:

final class ExampleStorage : NSTextStorage {

    private let container = NSTextStorage()

    override var string: String {
        return container.string
    }

    override func attributes(at location: Int, effectiveRange range: NSRangePointer?) -> [NSAttributedStringKey : Any] {
        return container.attributes(at: location, effectiveRange: range)
    }

    override func replaceCharacters(in range: NSRange, with str: String) {
        beginEditing()
        container.replaceCharacters(in: range, with: str)
        edited([.editedAttributes, .editedCharacters], range: range, changeInLength: (str as NSString).length - range.length)
        endEditing()
    }

    override func setAttributes(_ attrs: [NSAttributedStringKey : Any]?, range: NSRange) {
        beginEditing()
        container.setAttributes(attrs, range: range)
        edited([.editedAttributes], range: range, changeInLength: 0)
        endEditing()
    }
}

【讨论】:

  • 你能解释一下为什么你应该使用 NSTextStorage 作为后备存储吗?什么是性能问题?
  • 我没有做具体的调查。在一两年前我在 iOS 上的丰富编辑器实现的经验之后,这是一种上帝的感觉。您还可以在 NSMutableAttributedString 周围找到一些 memory-related issues
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多