I have a custom UITextView
that takes advantage of Apple's TextKit by defining a custom NSTextStorage
class, however, when I use my subclass for the custom text view's, text storage (as implemented below) and try opening any file greater than 20.0KB, the app crashes due to memory leakage: "Message from debugger: Terminated due to memory issue
".
Oddly enough, if I replace my custom BMTextStorage
with just a standard one, NSTextStorage
, the text loads instantly without any memory leakage and uses < 30MB of RAM. What is causing this?
TextView.swift
class TextView : UITextView {
required init(frame: CGRect) {
// If I replace the following line with simply
// "let textStorage = NSTextStorage()"
// I can open any file of any size and not have a memory leak
// issue, using only about 20-30MB of RAM. If I ran this code
// as is, it can open most files less than 20KB but will
// crash otherwise.
let textStorage = BMTextStorage()
let layoutManager = NSLayoutManager()
layoutManager.allowsNonContiguousLayout = true
let textContainer = NSTextContainer(size: CGSizeMake(.max, .max))
textContainer.widthTracksTextView = true
textContainer.heightTracksTextView = true
textContainer.exclusionPaths = [UIBezierPath(rect: CGRectMake(0.0, 0.0, 40.0, .max))]
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
super.init(frame: frame, textContainer: textContainer)
textStorage.delegate = self
layoutManager.delegate = self
}
}
BMTextStorage.swift
typealias PropertyList = [String : AnyObject]
class BMTextStorage : NSTextStorage {
override var string: String {
return storage.string
}
private var storage = NSMutableAttributedString()
override func attributesAtIndex(location: Int, effectiveRange range: NSRangePointer) -> PropertyList {
return storage.attributesAtIndex(location, effectiveRange: range)
}
override func replaceCharactersInRange(range: NSRange, withString str: String) {
storage.replaceCharactersInRange(range, withString: str)
edited([.EditedAttributes, .EditedCharacters], range: range, changeInLength: str.length - range.length)
}
override func setAttributes(attrs: PropertyList?, range: NSRange) {
storage.setAttributes(attrs, range: range)
edited([.EditedAttributes], range: range, changeInLength: 0)
}
override func processEditing() {
super.processEditing()
}
}
Wow.... weird, it got fixed when I changed the type of storage
to NSTextStorage
....
typealias PropertyList = [String : AnyObject]
class BMTextStorage : NSTextStorage {
overrride var string: String {
return storage.string
}
private var storage = NSTextStorage()
override func attributesAtIndex(location: Int, effectiveRange range: NSRangePointer) -> PropertyList {
return storage.attributesAtIndex(location, effectiveRange: range)
}
override func replaceCharactersInRange(range: NSRange, withString str: String) {
storage.replaceCharactersInRange(range, withString: str)
edited([.EditedAttributes, .EditedCharacters], range: range, changeInLength: str.length - range.length)
}
override func setAttributes(attrs: PropertyList?, range: NSRange) {
storage.setAttributes(attrs, range: range)
edited([.EditedAttributes], range: range, changeInLength: 0)
}
override func processEditing() {
super.processEditing()
}
}