Upload
hasegawa
View
413
Download
0
Embed Size (px)
Citation preview
メモリ管理
メモリの確保と解放
アプリケーションを起動したり,ウィンドウを開いたり,コンピュータは何をするにもメモリが必要
これらの作業において,いつメモリが必要になるのか突き詰めて行くとオブジェクトをインスタンス化したときということになる
オブジェクトをインスタンス化するということは,そのオブジェクトが必要な量のメモリを確保するという事
メモリにまつわる問題
メモリ解放のタイミング
メモリは確保するときは特に問題ない
必要なときに必要なだけ確保すればよい
問題は解放のとき
原則的には必要なくなったときに解放すればよい
しかし,いつ必要なくなったかわかるのか?
メモリにまつわる問題
メモリ解放のタイミング
ウィンドウ上のボタンにタイトルを設定するとする
タイトルは文字列なので,まず文字列のためのメモリを確保し,タイトルを設定する
このメモリはいつ解放すればいいのか?
ボタンに設定したすぐ後なのか?
この段階ではボタンがこの文字列オブジェクトを使っているかもしれないので,まだ解放できない
メモリにまつわるの問題
メモリの解放のタイミング
ボタン自体が解放されるときなら確実
そうなるとボタンが解放されるまで見張り続けなければいけない
確保したオブジェクトを自分だけが使っていれば,いつ解放すればいいか自分で決められるのでそれほど問題ではない
メモリリーク
もしメモリの解放を忘れた場合どうなるのか
そのメモリはアプリケーションが終了するまでずっと占領されてしまう
それが何度も繰り返して起こると,搭載されているメモリの量を超えてしまい,再起動しなければならない状態になる
この問題をメモリリークと呼ぶ
ガベージコレクションによるメモリ管理
ガベージコレクション(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
主にプラグインを利用するために用意された項目
アプリケーションの本体がガベージコレクションに対応していても,プラグインが対応しているかわからない
ガベージコレクションに対応していないプラグインでも読み込んで,実行できるようにする
ガベージコレクションの有効化
参照カウンタ
参照カウンタ
メモリ管理の手法としていろいろなフレームワークで使われているもの
基本的な考え方は,あるオブジェクトに対して,それが必要なら必要な人がつばをつけておく
必要なければ「要らない」と宣言しておく
参照カウンタ
参照カウンタ
誰からも必要とされなくなったら,オブジェクトは解放される
このときのつばを付ける事を,オブジェクトを参照する(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のインスタンスを作る
その状態で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メソッドを呼びだす
このとき保持されているオブジェクトがその場で解放されるかはわからない
まだ別のオブジェクトから保持されている可能性がある
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言語の文字列
%% %記号そのものを表現
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で指定された文字列を連結した新しい文字列を作成して,返します
文字列の操作
NSMutableStringの作成
可変文字でありNSMutableStringを使えば一度使ったインスタンス文字を変更できる
stringのメソッドでは空の文字列を作る
NSObject+ (id)string 空文字のインスタンスを作ります+ (id)stringWithString:(NSString*)string- (id)initWithString:(NSString*)string
文字列stringと同じ内容のインスタンスを作ります.
文字列の操作
文字列の追加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となる