[iPhone開発のネタ帳] loadView, viewDidLoad と viewDidUnload 2011/08/12

公開日: : iPad, iPhone ,

拙作のiPadアプリ タッチ!にほんちずタッチ!ヨーロッパ地図でUIWebViewControllerでWikipediaを表示したり、ランキング表示をしたりするようになってから、結構頻繁に didReceiveMemoryWarning や viewDidUnload が呼ばれるようになった。
これにより問題が起き、芋づる式にいろいろ調べることになってしまったのでそのメモを残してみる。

調べたこと

  • loadViewとviewDidLoad の使い分け
  • viewDidUnload で何が解放されるか
  • viewDidUnload が呼ばれてしまったらどんな対応をすればよいか
  • nibファイルを使わない(Interface Builderを使わない)開発(これはおまけ)

didReceiveMemoryWarning は、アプリケーションで使用しているメモリが多くなり、メモリ枯渇状態になったときに裏にいるviewをもつ UIViewController に対して呼ばれるという理解をしている。
didReceiveMemoryWarning のデフォルトの処理は UIViewController の view が解放できるかをチェックし、解放できると判断した場合、view を解放し、viewDidUnload を呼ぶ。
アプリとしては didReceiveMemoryWarning を受け取ったら解放できるメモリがあれば解放して、以降の処理が継続できるようにすべき。(でないとそのうちメモリが枯渇してアプリが強制終了される)
自分の記憶だと、didReceiveMemoryWarning の中である程度メモリを解放できていれば、view は解放されず、viewDidUnload も呼ばれない、だと思っていたがドキュメントによるとそういうことは書かれていないようだ。
ただ、実際に動かしてみるとdidReceiveMemoryWarning が呼ばれても必ず viewDidUnload まで到達するわけではないし、warning にはレベルがある気がするがそこまではまだ調べていない。

ちなみに手元の本にあたってみたところ、いくつかの本にもviewDidUnload の話があった。
はじめてのiPhone3プログラミング P.106 「善良なメモリ市民になる」によると、メモリ枯渇状態が発生し、nib ファイルがアンロードされると、viewDidUnload が呼ばれる。
nib は解放されるが、コード上で、例えば
@property (nonatomic, retain) IBOutlet UILabel *titleLabel;
などしている場合 retain カウンタが増えてしまっているので、viewDidUnload で手動で
self.titleLabel = nil;
をする必要がある。
このコードにより、実際は [iPhone SDK] Objective-C 2.0 のプロパティ が行われるので、コード上はただの nil の代入に見えるが、実際は release が行われて、retain カウントが減って0になり、オブジェクトは解放される。

self.titleLabel = nil;

titleLabel = nil;

は大きく異なる。(最初によくはまる問題)
ちなみに、このドット記法は Objective-C 2.0 で導入されたらしいけれども、上記のように分かりづらい問題が発生することがあり無用に問題を複雑にすることがあるので、アーロン・ヒレガス氏はMac OS X Cocoaプログラミング 第三版 P.124 で「私自身はこういった記法の追加によって言語が無駄にややこしくなったと考えています。このため、本書ではこの記法を採用していません」と書いている。

ということで、これを調べている間に見つけた
ヒビノアワ: iOS SDKのviewDidUnloadでoutletを解放するべきか否か
に対する回答としては、viewDidUnload で解放する必要がある(はず)。

iPhoneプログラミングUIKit詳解リファレンス P.116 にも viewDidUnload の記載があり、viewDidUnload でメモリを解放しようと書かれているが、もし不要なメモリがあればまずは didReceiveMemoryWarning で行うのが正しいと思われる。

このOSによる強制的な nib (というかview) の解放が行われた場合、そのままだと nib でロードしたオブジェクトがない状態になってしまうので、自動的に viewDidLoad が呼ばれることになる。
(viewDidLoad は通常UIViewControllerの生存期間中に一度しか呼ばれないが、viewDidUnload が呼ばれたときのみ2回以上呼ばれることになる)

自分は viewDidLoad にアプリのほとんどの初期化処理を書いていたが、実際にはErica女史が好んで使う UIViewControllerの loadView が先に呼ばれる。
loadView で実際何が行われているかなどはここでくわしく考察されている。
iPhoneアプリ開発、その(164) きっかけはself.view|テン*シー*シー

Interface Builder(IB) を使っている場合、UIViewControllerのプロパティnibNameにnibファイル名が書かれているはずなので、それをもとに loadView でUIViewControllerのviewプロパティにUIView が設定される。
UIViewController Class Reference
によると、view プロパティが設定されると、次に viewDidLoad が呼ばれる。
コードで init でオブジェクトを生成する場合には loadView 内に記述し、nib ファイルでロードするオブジェクトに関しては viewDidLoad を使うような使い分けをするような記憶があるが、しかしnibファイルでロードしたオブジェクトに対して、その後にコードで init する部品もいろいろある場合はどうすればよいのか。
上記の テン*シー*シー にも書かれているが、IBを使っている場合は、コードでUI部品を init している場合でも、このviewDidLoad に書いてしまっても良いと思っている。

ここで、ようやく本題のviewDidUnload の話になる。
viewDidUnload が呼ばれると、通常一度しか呼ばれない viewDidLoad が再度呼ばれてしまうので、上記のように viewDidLoad でいろいろ初期化している場合には2回以上初期化されてしまうことになってしまう。
このため、それに備えて viewDidUnload には viewDidLoad で確保しているオブジェクトなどを解放するコードを書いておく必要がある。
(もしくは、フラグを立てたりして二重に確保しないようにする)
自分は viewDidUnload に解放処理を書いていなかったので、2回目の viewDidLoad で二重に初期化処理が行われていろいろ問題が起きていたのだった。
ということで、一応解決。

ちなみに viewDidLoad と viewDidUnload と dealloc の関係はよく勘違いされていることがあるので、その場合はこちらをどうぞ。
Cocoaの日々: UIViewController でのメモリ管理見本
viewDidLoad と viewDidUnload は対になっていつでも呼ばれそうな気がするが、実際にはメモリ枯渇状態になったときしか viewDidUnload は呼ばれないので、通常の終了処理は dealloc に書いておく必要がある。(viewDidUnload だけに書いておいても、通常の流れでは呼ばれない)

このあたりのことは、iOS開発におけるパターンによるオートマティズム P.100 からくわしく書かれていた。

ちなみに IB を全く使わずにコーディングしたい場合、こちらを参照するのが良さそう。

IBや、ドット記法などは分かって使っていれば便利だけれども、これらを使わないでプログラミングすれば、もう少し複雑度は下がる気がするし、iPhone SDK勉強会で説明するネタも減りそうだ。
しかしAppleがサンプルプログラムなどで積極的に使っているのでなかなかそうはいかないと思うけれども。

いろいろ書いたので何か間違いがあればコメントください。

はじめてのiPhone3プログラミング
はじめてのiPhone3プログラミング Dave Mark Jeff LaMarche 鮎川 不二雄

ソフトバンククリエイティブ 2009-12-17
売り上げランキング : 36737

Amazonで詳しく見る by G-Tools

Mac OS X Cocoaプログラミング 第三版
Mac OS X Cocoaプログラミング 第三版 Aaron Hillegass アーロン ヒレガス 村上 雅章

ピアソン桐原 2009-11-01
売り上げランキング : 26181

Amazonで詳しく見る by G-Tools

iPhoneプログラミングUIKit詳解リファレンス
iPhoneプログラミングUIKit詳解リファレンス 所 友太 京セラコミュニケーションシステム株式会社

リックテレコム 2010-01-12
売り上げランキング : 6167

Amazonで詳しく見る by G-Tools

iOS開発におけるパターンによるオートマティズム
iOS開発におけるパターンによるオートマティズム 木下 誠

ビー・エヌ・エヌ新社 2011-02-09
売り上げランキング : 15835

Amazonで詳しく見る by G-Tools

関連記事

Apple Special Event 2017

2017年は仕事で死んでいたので、Special Eventの記事を書けていなかった。記録

記事を読む

no image

EDAMTimestamp と NSDate の相互変換

Evernote SDKを使ったアプリで EDAMTimestamp を DBに格納する前に NSD

記事を読む

no image

iTunes Connect でたらいまわし

iTunes Connect でしばらくたらい回しにあっている。さすがにひどいので記録しておく。 i

記事を読む

no image

iOSアプリの Ad Hoc 配布の悪夢から解放される TestFlight

面倒だった Adhoc 版のやりとりを格段に楽にしてくれる TestFlight | つくる社LL

記事を読む

[買ってみた] iPhone 11 Pro Max

iPhone 11 Pro Maxを発売日の2019/9/20に購入し、もうすぐ買ってから

記事を読む

iPadアプリおんぷちゃんでMiselu C.24 をサポートしました

Bluetoothキーボード兼iPadカバーの Miselu C.24 が届いたので、iPadアプリ

記事を読む

no image

[iOS SDK] CGRect 関連の便利機能

CGRect を使っていていつも忘れて調べてしまうのでメモしてみる。 CGRect の変数を拡大・

記事を読む

no image

[iPhone SDK] UILabel からポップアップメニューを表示してみる

今作っているアプリに UILabel を複数置いた画面があるが、その UILabelたちは編集

記事を読む

no image

[英単語学習法] MyShortcuts を使って通知センターからいろいろな辞書を引いてみる

MyShortcuts+Viewer 価格: ¥100

記事を読む

[iOS SDK] EverLearn 3Dタッチ対応

iPhone 7 Plus を購入したので、まずは EverLearn から、3Dタッチの対応を行い

記事を読む

Message

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

Wi-Fi6Eルータ TP-Link AXE5400購入

Wi-Fi6E を試してみたくなり、TP-Link AXE5

児童手当 認定請求書申請 2024 「請求者が養育をする18歳に達する日以降の最初の3月31日までの子の数」とは?

2024年に受給していない人には手紙が届くらしい。 電子申請も

Vision Proアプリ開発本 8/24、8/26に発売

Vision Proアプリ開発入門 P400が 8/24 に発売、V

Developer Strap が日本でも購入可能に

USアカウントでしか購入できなかった Vision Pro 用 De

Vision Pro カバーケースを買ってみた

[itemlink post_id="11629"]

→もっと見る

  • 2011年8月
    1234567
    891011121314
    15161718192021
    22232425262728
    293031  
PAGE TOP ↑