PR
Calendar
ビューコントローラー内のオブジェクトを明示的に廃棄しないと、ゴミのようなコントローラーが残っちゃいますよ、というお話です。
先日作成した簡易的なメッセージ送受信画面(ビュー)。
テキストフィールドにメッセージを書き込むか、ビュー下段にあるボタン(イメージビュー)をタップすると、クラウド上のデータベースにメッセージが登録され、受信者&送信者は一定間隔でデータベースからメッセージを受信するという仕組みです。
受信したメッセージは、画面の上段の受信用イメージビューとメッセージラベルにそれぞれ表示されます。
テストをしていたところ、1回目のビュー表示時は正しくメッセージが送受信されるのですが、2回目以降のビュー表示時は、メッセージを送信してもその結果が反映されない事象が発生しました。
先に結論を言ってしまうと、前ビューに戻る際にコントローラー内で使用しているTimerオブジェクトが動きっぱなしになっており、そのせいでコントローラーが廃棄されていないことが原因でした。
表示中のビューに紐づいているコントローラーと、イベントを受けるコントローラーが別々になってしまっていたせいでラベルなどの更新がうまくいっていませんでした。
今後同じツボにはまらないように、調査の中で得た知見等をまとめておきます。
調査の過程
事象を発見した直後に、ビューの動作とデバッガーからわかったことは以下の3点。
・2回目以降も初期表示時は正しくイメージとラベルは表示される
・送信したメッセージはデータベースに格納されている
・受信処理も正常終了していて、メッセージを格納する配列にも正しく登録されている
以上のことから、メッセージ送受信の仕組みは正しく動いており、ビューの再描画ができていないことがは結構早い段階でわかりました。
ビューに対して、setNeedsDisplay() 発行してみても変わらず。
Debug用にUILabelを継承した以下のようなクラスを作ってみました。
import UIKit
// UILabelのデバッグ用。
class TMLabel : UILabel {
fileprivate let color = UIColor.white
// プロパティの初期化
fileprivate func configure () {
backgroundColor = color
translatesAutoresizingMaskIntoConstraints = false
}
/// イニシャライザ
init () {
print(NSStringFromClass(type(of: self )).components(separatedBy: "." )[ 1 ] + ":" + #function )
super .init(frame: CGRect.zero)
configure()
}
/// イニシャライザ
override init (frame: CGRect ) {
print(NSStringFromClass(type(of: self )).components(separatedBy: "." )[ 1 ] + ":" + #function )
super .init(frame: frame)
configure()
}
/// イニシャライザ(StoryBoardやIntefaceBuilderでの生成)
required init ?(coder aDecoder: NSCoder ) {
print(NSStringFromClass(type(of: self )).components(separatedBy: "." )[ 1 ] + ":" + #function )
super .init(coder: aDecoder)
configure()
}
/// 制約の更新
override func updateConstraints () {
print(NSStringFromClass(type(of: self )).components(separatedBy: "." )[ 1 ] + ":" + #function )
super .updateConstraints()
}
/// 制約の更新要否のフラグを立てる
override func setNeedsUpdateConstraints () {
print(NSStringFromClass(type(of: self )).components(separatedBy: "." )[ 1 ] + ":" + #function )
super .setNeedsUpdateConstraints()
}
/// 必要に応じて制約の更新を即時実行する
override func updateConstraintsIfNeeded () {
print(NSStringFromClass(type(of: self )).components(separatedBy: "." )[ 1 ] + ":" + #function )
super .updateConstraintsIfNeeded()
}
/// レイアウトの更新
override func layoutSubviews () {
print(NSStringFromClass(type(of: self )).components(separatedBy: "." )[ 1 ] + ":" + #function )
super .layoutSubviews()
}
/// レイアウトの更新要否のフラグを立てる
override func setNeedsLayout () {
print(NSStringFromClass(type(of: self )).components(separatedBy: "." )[ 1 ] + ":" + #function )
super .setNeedsLayout()
}
/// 必要に応じてレイアウトの更新を即時実行する
override func layoutIfNeeded () {
print(NSStringFromClass(type(of: self )).components(separatedBy: "." )[ 1 ] + ":" + #function )
super .layoutIfNeeded()
}
/// 描画の更新
override func draw ( _ rect: CGRect ) {
print(NSStringFromClass(type(of: self )).components(separatedBy: "." )[ 1 ] + ":" + #function )
super .draw(rect)
}
/// 描画の更新要否のフラグを立てる
override func setNeedsDisplay () {
print(NSStringFromClass(type(of: self )).components(separatedBy: "." )[ 1 ] + ":" + #function )
super .setNeedsDisplay()
}
/// 描画の更新要否のフラグを立てる
override func setNeedsDisplay ( _ rect: CGRect ) {
print(NSStringFromClass(type(of: self )).components(separatedBy: "." )[ 1 ] + ":" + #function )
super .setNeedsDisplay(rect)
}
deinit {
print(NSStringFromClass(type(of: self )).components(separatedBy: "." )[ 1 ] + ":" + #function )
}
}
1回目・ビュー初期化時
TMLabel:init(coder:)
TMLabel:setNeedsDisplay()
TMLabel:setNeedsDisplay()
TMLabel:setNeedsLayout()
TMLabel:setNeedsDisplay()
TMLabel:init(coder:)
TMLabel:setNeedsDisplay()
TMLabel:setNeedsDisplay()
TMLabel:setNeedsLayout()
TMLabel:setNeedsDisplay()
TMLabel:init(coder:)
TMLabel:setNeedsDisplay()
TMLabel:setNeedsDisplay()
TMLabel:setNeedsLayout()
TMLabel:setNeedsDisplay()
TMLabel:setNeedsLayout()
TMLabel:setNeedsDisplay()
TMLabel:setNeedsLayout()
TMLabel:setNeedsDisplay()
TMLabel:setNeedsLayout()
TMLabel:setNeedsDisplay()
TMLabel:updateConstraints()
TMLabel:updateConstraints()
TMLabel:updateConstraints()
TMLabel:updateConstraints()
TMLabel:updateConstraints()
TMLabel:updateConstraints()
TMLabel:setNeedsLayout()
TMLabel:setNeedsLayout()
TMLabel:setNeedsLayout()
TMLabel:layoutSubviews()
TMLabel:layoutSubviews()
TMLabel:layoutSubviews()
TMLabel:updateConstraints()
TMLabel:updateConstraints()
TMLabel:updateConstraints()
TMLabel:updateConstraints()
TMLabel:updateConstraints()
TMLabel:updateConstraints()
TMLabel:layoutSubviews()
TMLabel:layoutSubviews()
TMLabel:layoutSubviews()
TMLabel:draw(_:)
TMLabel:draw(_:)
TMLabel:draw(_:)
1回目・メッセージ送信から受信時後
TMLabel:setNeedsLayout()
TMLabel:setNeedsDisplay()
TMLabel:setNeedsDisplay()
TMLabel:setNeedsDisplay()
TMLabel:updateConstraints()
TMLabel:updateConstraints()
TMLabel:layoutSubviews()
TMLabel:updateConstraints()
TMLabel:updateConstraints()
TMLabel:layoutSubviews()
TMLabel:draw(_:)
TMLabel:draw(_:)
TMLabel:draw(_:)
2回目・ビュー初期化時
TMLabel:init(coder:)
TMLabel:setNeedsDisplay()
TMLabel:setNeedsDisplay()
TMLabel:setNeedsLayout()
TMLabel:setNeedsDisplay()
TMLabel:init(coder:)
TMLabel:setNeedsDisplay()
TMLabel:setNeedsDisplay()
TMLabel:setNeedsLayout()
TMLabel:setNeedsDisplay()
TMLabel:init(coder:)
TMLabel:setNeedsDisplay()
TMLabel:setNeedsDisplay()
TMLabel:setNeedsLayout()
TMLabel:setNeedsDisplay()
TMLabel:setNeedsLayout()
TMLabel:setNeedsDisplay()
TMLabel:setNeedsLayout()
TMLabel:setNeedsDisplay()
TMLabel:setNeedsLayout()
TMLabel:setNeedsDisplay()
TMLabel:layoutSubviews()
TMLabel:layoutSubviews()
TMLabel:layoutSubviews()
TMLabel:draw(_:)
TMLabel:draw(_:)
TMLabel:draw(_:)
2回目・ メッセージ送信から受信時後
TMLabel:setNeedsLayout()
TMLabel:setNeedsDisplay()
TMLabel:setNeedsLayout()
TMLabel:setNeedsDisplay()
TMLabel:setNeedsDisplay()
TMLabel:setNeedsLayout()
TMLabel:setNeedsDisplay()
TMLabel:setNeedsLayout()
TMLabel:setNeedsDisplay()
TMLabel:setNeedsLayout()
TMLabel:setNeedsDisplay()






Timerイベントをclassメッソドに対して実… 2020.11.30
プログラムでviewを閉じる方法 2020.10.19
Dictionaryって 2020.04.06