Rutger Huijsmans Rutger Huijsmans - 4 months ago 48
Swift Question

UITextView memory leak when line breaks are present

My whole app freezes up when line breaks are added to the string I want to fill up a UITextView with.

This freezes up the app:

let testStringLineBreaks = "foooooo \n \n barrrrrr"
self.biographyTextView.text = testStringLineBreaks


This doesn't freeze up the app:

let testStringNoLineBreaks = "foooooobarrrrrr"
self.biographyTextView.text = testStringNoLineBreaks


Why? And how do I fix it? I need to be able to use linebreaks on my UITextView as well because this is where users fill in their profile description.

enter image description here

The app uses about 60 MB. However when it gets stuck on the line:

self.biographyTextView.text = testStringLineBreaks


It keeps going up with about 2 mb per second. I killed the app at 183 mb.

It only happens on iOS 8 and not on iOS 9.

import UIKit

class GFTextView: UITextView {
let bottomBorder = CALayer()

@IBInspectable var haveBottomBar:Bool?

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.textContainer.lineFragmentPadding = 0
}

func addBottomBar(){
bottomBorder.frame = CGRectMake(0.0, self.frame.size.height - 1, self.frame.size.width, 1.0);
bottomBorder.backgroundColor = UIColor.grayColor().CGColor
self.layer.addSublayer(bottomBorder)
}

override func becomeFirstResponder() -> Bool {
let shouldBecomeFirstResponder = super.becomeFirstResponder()
bottomBorder.backgroundColor = shouldBecomeFirstResponder ? Constant.ui_applicationMainColor.CGColor : Constant.ui_applicationMainColor.CGColor
return shouldBecomeFirstResponder
}

override func resignFirstResponder() -> Bool {
let shouldResignFirstResponder = super.resignFirstResponder()
bottomBorder.backgroundColor = shouldResignFirstResponder ? UIColor.grayColor().CGColor : Constant.ui_applicationMainColor.CGColor
return shouldResignFirstResponder
}

override func layoutSubviews() {
super.layoutSubviews()
if let font = self.font {
self.font = GFFont.fontForDefaultFontName(font.fontName, size: font.pointSize)
} else {
self.font = UIFont.systemFontOfSize(12)
}
bottomBorder.backgroundColor = UIColor.grayColor().CGColor
}
}

Answer

Based on the fact that you only get the error from your custom UITextView subclass, the problem is obviously from your implementation.

The most likely cause is your implementation of the layoutSubview function. The only types of things you should do in that overridden function is to adjust frames as needed.

Setting the font is probably causing a recursive call to layoutSubviews or something similar.

Setting the background color should be done in the init function. Setting the font should be done later since it depends on the value of a property that won't be set when init is called. The best place to set the font is in an override of the font property's setter.