[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

関連記事

no image

[iPhone SDK] Apple Push Notifiction Service (APNs)対応 その1

Apple Push Notification Service (以下、APNs) を使ったiPho

記事を読む

ドラム譜学習アプリ「ドラムちゃん」を紹介いただきました

Echodelic さん、Appliv さん、キッズアプリCOMさんで ドラム譜学習アプリ ドラムち

記事を読む

Apple Special Event 2010

2010年は、1月に初代 iPad が発表され、6月にWWDCでiPhone4が発表されるという、

記事を読む

no image

iPhoneアプリビジネス本 The Business of iPhone App Development

iPhoneアプリを売るための情報が詰まった本。 ここまでやるか、というくらいの情報が詰まっている。

記事を読む

iOS9から[NSLocale preferredLanguages] の出力が変更された

以前ツイートした内容を自分で忘れていて検索する羽目になったのでブログにも書いておく。iOS9から、

記事を読む

no image

[iOS] アプリ名を変更したところなぜかビルドが通らなくなり苦労した話

アプリ名にiPadと入れるとRejectされる に書いたとおり、アプリ名に iPad が入っていた

記事を読む

no image

iPhone SDK勉強会

iPhone 開発の勉強会をしよう、ということになったので、それ向けにメモを書いてみる。相手はいろい

記事を読む

Parse から Heroku, AWSへの移行ガイド

2017年1月28日にParseのサービスが終了するという衝撃的なニュース(Moving On)

記事を読む

no image

[iOS SDK] Pebble腕時計対応iOSアプリを作る

英単語学習アプリ WordLearnをリリースしましたに書いたけれども今Pebble腕時計対応iOS

記事を読む

とりあえず iPhone 6 Plus に対応する

iPhone 6 Plus は対応したアプリでないと画面が自動的に拡大されてらくらくホンぽい表示にな

記事を読む

Message

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

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

Apple Developer Program更新 2024

今年も更新した。 昨年はブログに記録し忘れたらしい。 今

ポモドーロテクニック用物理タイマーならTime Timer

会社ではなかなか自由に時間を使えないが、家で読書や作業をする

DELL 32インチディスプレイ U3223QE 購入

Dell U3223QE は解像度 3840x216

WWDC 2023 Vision Pro発表

2023/6/5 (日本時間 2023/06/06 2AM)のWWD

M1 MacBook Air を Venturaにアップデートする

M1 MacBook Air を macOS Montere

→もっと見る

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