67
第6回iPhone輪講 Lesson12 Big Waveに乗った人の図

Cocoa Pro6

Embed Size (px)

Citation preview

第6回iPhone輪講 Lesson12̃

Big Waveに乗った人の図

Lesson12メモリ管理

メモリ管理

メモリの確保と解放

アプリケーションを起動したり,ウィンドウを開いたり,コンピュータは何をするにもメモリが必要

これらの作業において,いつメモリが必要になるのか突き詰めて行くとオブジェクトをインスタンス化したときということになる

オブジェクトをインスタンス化するということは,そのオブジェクトが必要な量のメモリを確保するという事

メモリ管理

メモリの確保と解放

メモリの量は有限なので確保し続けたらいつかはなくなる

そこで必要なくなったメモリは解放しなくてはいけない

これでそのメモリは別のところで再利用できる

メモリにまつわる問題

メモリ解放のタイミング

メモリは確保するときは特に問題ない

必要なときに必要なだけ確保すればよい

問題は解放のとき

原則的には必要なくなったときに解放すればよい

しかし,いつ必要なくなったかわかるのか?

メモリにまつわる問題

メモリ解放のタイミング

ウィンドウ上のボタンにタイトルを設定するとする

タイトルは文字列なので,まず文字列のためのメモリを確保し,タイトルを設定する

このメモリはいつ解放すればいいのか?

ボタンに設定したすぐ後なのか?

この段階ではボタンがこの文字列オブジェクトを使っているかもしれないので,まだ解放できない

メモリにまつわるの問題

メモリの解放のタイミング

ボタン自体が解放されるときなら確実

そうなるとボタンが解放されるまで見張り続けなければいけない

確保したオブジェクトを自分だけが使っていれば,いつ解放すればいいか自分で決められるのでそれほど問題ではない

メモリリーク

もしメモリの解放を忘れた場合どうなるのか

そのメモリはアプリケーションが終了するまでずっと占領されてしまう

それが何度も繰り返して起こると,搭載されているメモリの量を超えてしまい,再起動しなければならない状態になる

この問題をメモリリークと呼ぶ

ガベージコレクションによるメモリ管理

ガベージコレクション(Garbage Collection)とは何か?

メモリ上の使われなくなったオブジェクトをゴミとみなして定期的にそれを回収し,メモリ領域を再利用できるようにする

どのように,あるオブジェクトがゴミかそうでないかを見分けるのかが問題

ガベージコレクションによるメモリ管理

ゴミでない(まだ使われている)オブジェクトを考える

あるオブジェクトが使われている→他のオブジェクトがそのオブジェクトを必要としている

オブジェクトを必要としていることがプログラム中でどうなるのかというと,そのオブジェクトを参照しているということ

つまりインスタンス変数として参照をキープしている

ガベージコレクションによるメモリ管理

ゴミでない(まだ使われている)オブジェクトを考える

あるオブジェクトが使われている→他のオブジェクトがそのオブジェクトを必要としている

オブジェクトを必要としていることがプログラム中でどうなるのかというと,そのオブジェクトを参照しているということ

つまりインスタンス変数として参照をキープしている

ゴミとなるオブジェクトは「誰からも参照されていないオブジェクトのこと」

と,言い切れるものではない

ガベージコレクションによるメモリ管理

A

B C

D E

次のような状況が考えられる

いま5つのオブジェクトがある

Aは必要なオブジェクトでB,Cを参照している

従ってBとCはゴミではない

DとEはゴミだが,互いに参照している

「参照されていないオブジェクトがゴミ」というルールだとDとEをゴミとして認識する事ができない

オブジェクトの参照だけではゴミか決定できない

ガベージコレクションによるメモリ管理

これを解決するには「必要なオブジェクト」を決める必要がある

そこから参照されているオブジェクトはゴミでないとする

すべての必要となるオブジェクトの参照を辿った後,まだ必要とされてないオブジェクトをゴミとする

これがガベージコレクションのゴミ回収ルールである

このような絶対に必要なオブジェクトは「ルートオブジェクト」と呼ばれる

A

B C

D E

ルートオブジェクト(NSApplication)

A,B,Cは必要なオブジェクト

D,Eはゴミ

ガベージコレクションによるメモリ管理

Cocoaの場合のルートオブジェクトは何か

まずアプリケーションそのものを表すオブジェクト

これは「NSApplication」というクラスのインスタンスになる

またメソッドやクラスの外で宣言しているオブジェクトも必要なものになる

ガベージコレクションによるメモリ管理

このゴミ決定ルールを使う事でのガベージコレクションの挙動

1.まずアプリケーションが起動する

2.しばらく好き勝手にオブジェクトを作り,できたものは放っておく

3.ある程度の量のオブジェクトを作ったら,一旦アプリケーションの実行を停止し,ガベージコレクションを発動する

ガベージコレクションによるメモリ管理

このゴミ決定ルールを使う事でのガベージコレクションの挙動

4.ガベージコレクションでは,すべてのルートオブジェクトから参照を辿って行き,必要とされるオブジェクトにマークをつける

5.最後までマークがつかなかったオブジェクトをゴミと見なし,回収して解放する

6.使用可能なメモリ領域が広がったところでアプリケーション実行の再開

ガベージコレクション

ガベージコレクション有効化

ガベージコレクションは標準では無効化されているので有効化する

Xcodeで適当なプロジェクトを開く

[プロジェクト]→ [プロジェクト設定を編集]メニューを選択

プロジェクト情報パネルが開く

ガベージコレクション

ガベージコレクションの有効化

情報パネルの左から2番目「ビルド」のタブを選択

ここでビルドの設定ができる

「Objective-C Garbage Collection」という項目を探す

見つかったら値の設定をする

値はUnsupported,Supported,Requiredの3種類

ガベージコレクションの設定

Unsupported

ガベージコレクションを無効化する

Supported

主にプラグインを利用するために用意された項目

アプリケーションの本体がガベージコレクションに対応していても,プラグインが対応しているかわからない

ガベージコレクションに対応していないプラグインでも読み込んで,実行できるようにする

ガベージコレクションの有効化

ガベージコレクションの設定

Required

すべてコードがガベージコレクション対応のものとして扱う

自分で通常のアプリケーションを作る場合はこれを選択する

ガベージコレクションの有効化

参照カウンタ

参照カウンタ

メモリ管理の手法としていろいろなフレームワークで使われているもの

基本的な考え方は,あるオブジェクトに対して,それが必要なら必要な人がつばをつけておく

必要なければ「要らない」と宣言しておく

参照カウンタ

参照カウンタ

誰からも必要とされなくなったら,オブジェクトは解放される

このときのつばを付ける事を,オブジェクトを参照する(refer)または,保持する(retain)と呼ぶ

参照された回数を数えて管理するので,参照カウンタと呼ばれる

retain,releaseメソッド

参照カウンタの手法では2つの操作が基本となる

オブジェクトを参照するための操作

参照をやめるための操作

Cocoaではこれらの操作に対応するメソッドが用意してある

参照するために使うのはretain

参照をやめるために使うのはrelease

これらはルートオブジェクトであるNSObjectのメソッド

オブジェクトの確保から解放までの流れ

retainとreleaseでオブジェクトの確保から解放までが,どのように管理されているかの流れ

AppControllerがボタンにタイトルを設定するという処理

オブジェクトの確保から解放までの流れ

確保から解放までの流れ1

AppControllerは,タイトルを表す文字列オブジェクトを作る

このとき文字列オブジェクトの参照カウンタは1である

インスタンス化されたオブジェクトの参照カウンタの初期値は1になる

0だった場合,いきなり解放される

AppController

@”Push Me!”インスタンス作成

参照カウンタ=1

オブジェクトの確保から解放までの流れ

確保から解放までの流れ2

AppControllerは setTitle:といったメソッドを使いボタンにタイトルを設定する

ボタンはメソッドの中で,この文字列オブジェクトはまだ必要だと判断してretainメソッドを呼び出す

これで参照カウンタは1つ増えて2になる

ボタンではこのオブジェクトを指し示すための変数をキープしてく必要がある

AppController @”Push Me!”

タイトル設定

ratain

参照カウンタ=2

Push Me!

オブジェクトの確保から解放までの流れ

確保から解放までの流れ3

AppControllerからすると,このオブジェクトはもう必要ない

そこでreleaseメソッドを呼び出し参照をやめる

参照カウンタは1減って1となる

これ以降AppController側では文字列オブジェクトのための変数を捨ててしまってもかまわない

AppController @”Push Me!”

release

参照カウンタ=1

Push Me!

オブジェクトの確保から解放までの流れ

確保から解放までの流れ4

ある程度処理が進み,ユーザがウィンドウを閉じたとする

ウィンドウ上のボタンも解放される

ボタンは解放されるとき自分が参照していた文字列オブジェクトに対して必要なくなるのでreleaseメソッドを呼び出す

参照カウンタが0になるので,文字列オブジェクトは解放される

@”Push Me!”参照カウンタ=0

Push Me!

release

クラスのインスタンス化

クラスのインスタンス化と初期化

クラスのインスタンス化をおこなうメソッドはalloc

これを呼ぶ事でインスタンス化(オブジェクトのためのメモリ確保)が行われる

allocはクラスメソッド

インスタンス化したいクラス名を指定してallocを呼ぶことになる

クラスのインスタンス化

クラスのインスタンス化と初期化

allocでインスタンス化したら,必ず初期化をしなくてはならない

インスタンス変数の値の初期値の設定や,その他インスタンスを使う前にやる必要がある処理を行ってくれる

初期化するには初期化メソッドを呼ぶ

初期化メソッドはクラスごとに用意されており,どれか適切なものを選ぶ事になる

NSObject+ (id) allocクラスをインスタンス化します.返り値はそのインスタンスオブジェクトになります.

クラスのインスタンス化

クラスのインスタンス化と初期化

initという一番基本的な初期化メソッドがある

NSObjectが持っているメソッドで,特別な初期化メソッドを呼ぶ必要がないときはこれを呼ぶ

NSObject- (id) initiインスタンスを初期化します

クラスのインスタンス化

クラスのインスタンス化と初期化

インスタンス化の基本はallocとinitを続けて呼ぶ事

MyObjectというクラスのインスタンス化の例

[[クラス�alloc] init]と重ねて呼ぶのがインスタンス化の基本のひとつ

このように初期化すると,参照カウンタは1となる

誰かがreleaseを呼ばない限り解放されない

クラスのインスタンス化

自動解放

一時的に作ったにすぎないオブジェクトに自動的にreleaseを送るための自動解放と呼ばれる仕組みが存在する

自動解放を使うと,その処理が終わった後,適切な場所でreleaseメソッドを呼んでくれる

自動解放を使うにはautoreleaseというメソッドを呼ぶ

クラスのインスタンス化

自動解放

MyObjectをインスタンス化して,さらに自動解放する例

[[[クラス名 alloc] init]autorelease]と3段重ねで呼ぶ

インスタンス化のときautoreleaseを呼ぶかは,そのオブジェクトを一時的にしか使わないのか,しばらく使うのかで決定する

NSObject- (id) autoreleaseインスタンスを自動解放します.返り値として自分自身を返します.

インスタンスの解放

参照カウンタが0になると,そのインスタンスは解放される

解放される直前にdeallocというメソッドが呼び出される

サブクラスでこのメソッドを上書きする事で,解放されるときに必要な処理を行う事ができる

NSObject- (void) deallocインスタンスが解放される直前に呼び出されます.

インスタンスの解放

deallocの主な仕事はインスタンス変数の解放

もしクラスの中でなんらかのインスタンスを保持していたらここで必ず解放する

そうでないとメモリリークが起きる

deallocの実装例

ここではdocumentというインスタンス変数のreleaseメソッドを呼び出している

このようにインスタンス変数を解放している

メモリ管理の定石

一般的なオブジェクト

NSAutoreleasePool

自動解放はNSAutoreleasePoolというクラスによって実現されている

例えるのなら時限付きゴミ袋

一般的なオブジェクト

NSAutoreleasePool

自動解放をおこなうには,まずNSAutoreleasePoolのインスタンスを作る

その状態でautoreleaseメソッドを呼び出すと,そのインスタンスがNSAutoreleasePoolに登録される

最後にNSAutoreleasePoolを解放すると,登録されているすべてのreleaseメソッドを呼び出す

NSAutoreleasePoolによる自動解放の実現

autorelease

release

NSAutoreleasePoolを作成

autoreleaseを呼ぶとNSAutoreleasePoolに追加

NSAutoreleasePoolが解放されるとすべての

オブジェクトにreleaseを送る

NSAutoreleasePoolの作成と解放のタイミング

自動解放を行うときNSAutorelesePoolのインスタンスは誰がいつ用意するのか

Cocoaアプリケーションを立ち上げると,その内部でイベントループと呼ばれるループが回る事になる

イベントループはユーザからのキーボード入力や,マウス操作を待ち受けるループのこと

マウスのクリックの通知がくると,ユーザインタフェースのどの部分がクリックされたかを調べ,最終的に登録されたアクションを呼び出したりする

NSAutoreleasePoolの作成と解放のタイミング

イベントループの頭でNSAutoreleasePoolのインスタンスを作成し,最後に解放している

イベントループの内部であれば,いつでも自動解放を使える

Cocoaのアプリケーションではほとんどの処理がInterface Builderで登録したアクションをトリガーとして始まる

アクションはイベントループが呼び出されて実行が始まり,終わるとループが一周する

アクション実行中は常にNSAutoreleasePoolのインスタンスがある

アクションメソッドが終わると自動解放されたオブジェクトは解放される

アクションの呼び出しとNSAutoreleasePool

イベントループ開始NSAutoreleasePool作成

アクションメソッド実行

インスタンスを作成して自動解放するとNSAutoreleasePoolに登録される

アクションメソッド終了まだ自動解放されたインスタンスは解放されない

NSAutoreleasePool解放自動解放されたインスタンスは,ここで解放される

メモリ管理の定石のまとめ

オブジェクトを保持するか,一時的に使うだけか判断する

インスタンスを作るとき,または他で作られたオブジェクトをメソッドの引数として受け取ったときは,そのオブジェクトが保持する必要があるのか,一時的に使うだけかを判断する

そのメソッドの内部でしか使わないのか,メソッドが終了した後でも何度も使うかの違い

メモリ管理の定石のまとめ

保持するオブジェクトは,参照カウンタを上げる

保持する必要があるオブジェクトに対しては参照カウンタを1つ上げておく

他からもらったオブジェクトの場合はretainメソッドを呼んでやる

自分でインスタンスを作成するときはalloc-initの組み合わせで作るまたは,init...で始まる初期化メソッドを使う

忘れずにこのオブジェクトをインスタンス変数としてキープしておく

メモリ管理の定石のまとめ

一時的なオブジェクトは自動解放する

一時的にしか使わないオブジェクトは自動解放しておく

インスタンスの作成をalloc-init-releaseの3段重ねで行う

またはそれぞれのクラスで自動解放されたインスタンスを作成するためのメソッドを使う

メソッドの引数として渡された場合は特に何もする必要が無い

そのオブジェクトに対する責任は,それを作ったクラスが持つ事になる

メモリ管理の定石のまとめ

deallocですべて参照カウンタを下げる

保持しているオブジェクトは最後に解放しなくてはならない

それをおこうなうチャンスがdeallocメソッド

deallocメソッドを上書きして,保持しているインスタンス変数すべてのreleaseメソッドを呼びだす

このとき保持されているオブジェクトがその場で解放されるかはわからない

まだ別のオブジェクトから保持されている可能性がある

Lesson13文字列

NSStringとNSMutableString

Cocoaの文字列クラスがNSStringとNSMutableString

NSStringクラスは内容を変更しない固定の文字列を表す

NSMutableStringクラスは内容の変更できる可変の文字列である

NSObject

NSMutableString

NSString

NSStringとNSMutableString

NSString,NSMutableString

NSStirngは一度インスタンスを生成するとテキストを変更できない

ウィンドウやボタンのタイトルを取得するときのように,勝手に内容を変更されたくないときに使う

文字列の追加や削除などの編集を行いたい場合はNSMutableStringを使う

NSMutableStringはNSStringを継承しているので,NSStringに対して行える操作はすべて使える

文字列の作成

@””を使った作成

C言語ではダブルクォーテーション(“”)でくくることにより文字列が作成できた

ダブルクォーテーションの前に@をつける事で,C言語の文字列(charの配列)ではなくNSStringのインスタンスとなる

文字列の作成

日本語文字からの作成

NSStringの作成はアルファベットでしか使えないので,日本語文字列を作る事ができない

しかしデバッグなどで日本語が使いたいときは,C言語の文字列からNSStringのインスタンスを作るメソッドstringWithCString:encoding:または,initWithCString:encoding:を使う

文字列の作成

前者の方は自動解放されたインスタンスを作成する

後者の方はallocの後で呼び出す初期化メソッドである

NSString

+ (id) stringWithCString:encoding:(const char*)cString encoding:(NSStringEncoding)encding

- (id) initWithCString:encoding:(const char*)cString encoding:(NSStringEncoding)encding

C言語の文字列cStringとそのテキストエンコーディングencodingを指定して,インスタンスを作ります

文字列の作成

これらのメソッドは1つめの引数にC言語の文字列を指定する

2つめの引数でエンコーディングを指定する

C言語の文字列はソースコードファイルのテキストエンコーディングがそのまま使われる

ファイルのテキストエンコードを日本語のものにして,それを2つ目の引数に指定してやればよい

+ (id) stringWithCString:encoding:(const char*)cString encoding:(NSStringEncoding)encding

文字列の作成

例の紹介

まずソースコードファイルのテキストエンコーディングを設定する

Xcodeの[表示]→[テキスト]→[ファイルエンコーディング]メニューを使う

[日本語(Shift JIS)]にして,以下のようなコードで文字列を作る

ポイントは引数の文字列には@をつけない事,エンコーディングの引数をファイルのエンコーディングと同じにする事

フォーマットからの作成

フォーマットを指定して文字列作成もできる

フォーマットはprintf文で指定するものと同じ置換子を使うもの

stringWithFormat:というメソッドを使う

printfのときと同様に可変引数を使う

フォーマット自体もNSStringで指定する事に注意NSString

+ (id) stringWithFormat:(NSString*)format...

- (id) initWithFormat:(NSString*)format...

フォーマットでformatを指定して,インスタンスを作ります.

フォーマットからの作成

このメソッドで使える置換子はprintfのものとほぼ同じ

ただNSStringのインスタンスも「%@」という置換子で指定できる置換子 説明

%@ NSStringのインスタンス

%d,%D,%i 整数

%u,%U 符号なしの整数

%x 符号なしの整数を,小文字の16進数で表現

%X 符号なしの整数を,大文字の16進数で表現

%f 小数

%c 文字

%s C言語の文字列

%% %記号そのものを表現

フォーマットからの作成

stringBは Mac Book Pro

となる

stringCは80%となる

stringDは0x50となる

NSRange

構造体NSRangeはある範囲を表す

10文字の配列の「5文字目から3文字分」という具合で範囲指定したいときに使う

NSString

NSMakeRange(unsigned int location, unsigned int length);

範囲開始位置locationと,長さlengthを指定してNSRangeを作ります

実行結果はlocation is 0, length is 5

となる

文字列の操作

文字列の長さを調べるにはlengthメソッドを使うNSObject

- (unsigned int)length

文字列の長さを,文字数で取得します

実行結果はlength is 8となる

文字列の操作

2つの文字列の連結

ある文字列の後ろに,別の文字列をくっつけて新しい文字列を作るもの

stringByAppendingString:,stringByAppendingFormat:というメソッドを使う

NSObject- (NSString*)stringByAppendingString:(NSString*)string自分自身に,文字列stringを連結した新しい文字列を作成して,返します- (NSString*)stringByAppendingFormat:(NSString*)format, ...自分自身に,formatで指定された文字列を連結した新しい文字列を作成して,返します

文字列の操作

2つの文字列の連結

実行結果はMac OS X leopard

となる

実行結果はleopard10.5となる

文字列の操作

NSMutableStringの作成

可変文字でありNSMutableStringを使えば一度使ったインスタンス文字を変更できる

stringのメソッドでは空の文字列を作る

NSObject+ (id)string 空文字のインスタンスを作ります+ (id)stringWithString:(NSString*)string- (id)initWithString:(NSString*)string

文字列stringと同じ内容のインスタンスを作ります.

文字列の操作

NSMutableStringの作成

stringWithString:を使えばNSStringからNSMutableStingを作る事ができる

文字列の操作

文字列の追加NSmutableString- (void)appendString:(NSString*)string文字列stringを追加します- (void)appendFormat:(NSString*)format, ...formatで指定された文字列を追加します

実行結果はMac OS Xとなる

文字列の操作

文字列の挿入��NSmutableString

- (void)insertString:(NSString*)string atIndex:(unsigned)index

文字列stringを,インデックスindexに挿入します.

実行結果はMac Book Pro

となる

文字列の操作

文字列の削除NSmutableString

- (void)deleteCharactersInRange:(NSRange)range

範囲rangeで指定した文字列を削除する.

実行結果はacとなる

おわり