ポインタのポインタ
ポインタのポインタは何のために使うか良くわからなかったけど、使ってみると意外と便利だった。
例えば何か変数の初期化をするような関数を作るとする。
- (void) createView:(UIView*)view1 otherView:(UIView*)view2 { // なんか処理 view1 = [[UIView alloc] initWithFrame:CGRectZero]; view2 = [[UIView alloc] initWithFrame:CGRectZero]; }
この関数は2つのビューを作成するもの。2ついっぺんに return することはできないので、もらったポインタにたいして処理をするものとする。
そうすると、上の書き方だと問題がある。例えば、下のようにこの関数を使うとする。
- (void)hoge { UIView *view1, *view2; [self createView:view1 otherView:view2]; NSLog(@"view1:%@ view2:%@", view1, view2); }
実はこのように使うと、5行目のログはnilが出力されるはず。createView 内で alloc しているので、新しいメモリアドレスで UIView の分のメモリを確保するのだ。元のポインタ変数の指し示すアドレスは変わらないため、元のポインタには変更が反映されない。
そこで、ポインタのポインタを使う。ポインタのポインタからポインタに対して操作すると、ちゃんとポインタに変更が反映される。何言ってるかわからないと思うけど、下のコードを見て欲しい。自分もObj-Cやり始めてひが浅いのでうまい説明の仕方があれば是非教えてください。
// 引数にポインタのポインタを指定 - (void) createView:(UIView**)view1 otherView:(UIView**)view2 { // 処理するときにはポインタを操作 *view1 = [[UIView alloc] initWithFrame:CGRectZero]; *view2 = [[UIView alloc] initWithFrame:CGRectZero]; } - (void)hoge { UIView *view1, *view2; // 関数にはポインタのアドレスを渡す [self createView:&view1 otherView:&view2]; NSLog(@"view1:%@ view2:%@", view1, view2); }
こんな感じで書けば呼び出し元の変数にも変更が反映される。Flyweightパターンとか、この書き方知らないとしんどくなりそう。
alloc とか init とか retain とか
いろいろメモリ管理ハマったので。
UIView を例にしてみる。UIView はビュー要素のコンテナみたいなもん。こんな感じで使う。
UIView *unko = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; [self addSubview:unko]; [unko removeFromSuperview]; [unko release];
1行目でインスタンス生成して
2行目でそのインスタンスをビューに追加して
3行目でインスタンスをビューから削除して
4行目でメモリ解放を行う。
このとき、unkoをretainしちゃダメっぽい。
ObjC ではオブジェクトは retain カウントっていう値を持っていて、これが 0 になったときにガベコレされるっていう機構を持ってるのだとか。
どうやら UIView は、というか ObjC の基本クラス群は alloc とか init〜 とかすると、retain カウントが1増えるらしい。ということは、alloc やら init やらした時点で retain カウントは +1 なんですな。
このカウントは release してやると -1 されるらしい。4行目にあるやつですな。
つまり、UIView *unko = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)] retain]; とかって書くと、2回 release してやらんと解放されない。
それから、このretainカウントは、addSubview した時点でも +1 される。つまり、追加するコンテナ(今回はself)がretainカウントを増やしているわけですな。なので、逆をいうと removeFromSubview では retain カウントが減るわけ。
じゃあ、これをクラスかなんかで書くとどうなるか。
Hoge.h
@interface Hoge : UIView { UIView *rView; UIView *aView; } @property (retain) UIView *rView; @property (assign) UIView *aView;
こっちが定義クラスで
Hoge.m
#import "Hoge.m" @implementation Hoge @synthesize rView; @synthesize aView; - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.rView = [[UIView alloc] initWithFrame:frame]; [self addSubview:rView]; self.aView = [[UIView alloc] initWithFrame:frame]; [self addSubview:aView]; } return self; } - (void)dealloc { [aView removeFromSuperview]; [aView release]; aView = nil; [rView removeFromSuperview]; [rView release]; rView = nil; [super dealloc]; } @end
こっちが実装クラス。
ちなみに、これはメモリリークを起こす。
その前に property ディレクティブについてちょっと説明すると、アクセサメソッドを自動で作ってくれる機能のことらしい。
どういうことかというと、ObjC ではクラスのメンバ変数を作った時には基本的にはプライベートでアクセスできなくて、パブリックなアクセサメソッド作ってアクセスするのが一般的らしい。このアクセサメソッドを自動化したのが @property ディレクティブだそうな。
例えば
@interface Foo : NSObject { @private int bar; } @end
この場合 bar にアクセスするためには
- (void)setBar:(int)inValue;
- (int)getBar;
みたいなのを作ってメソッド経由でアクセスするのだけど、下のように
@interface Foo : NSObject { @private int bar; } @property int bar; @end
@property ディレクティブをつけてあげて、実装クラス側で @synthesize bar とかってやってあげると、このアクセサメソッドが自動で作られるってわけ。自力で実装した場合には [hoge getBar]; とかって書いていたのが、hoge.bar でアクセスできるようにもなったりする。
それで、このpropertyディレクティブにはもう少し機能があって、(retain)とか(assign)っていうものを付け足せる。
@interface Foo : NSObject { @private UIView *rView; UIView *aView; } @property (retain) UIView *rView; @property (assign) UIView *aView; @end
これを書くとどうなるかっていうと、assignは基本的には何も無い。こんな感じのアクセサメソッドができるっぽい。
- (void)setAView:(UIView*)inView { aView = inView; } - (UIView*)getAView { return aView; }
retain を書くと、セットされたときに retain を自動的につけてくれるっぽい。
- (void)setRView:(UIView*)inView { rView = [inView retain]; } - (UIView*)getRView { return rView; }
ちなみに、このセッターが発動するには、self.hoge というように、ドット演算子でアクセスしなければならないという決まりがある。
self.rView = hogeView;
の場合は上のセッターがコールされ retain されるが、
rView = hogeView;
の場合はセッターがコールされないので、ただの代入になる。
さて、前置きが長くなってしまったが、本題にもどる。
メモリリークを起こしてしまうコードのことだが、retain属性を持ってる rView に注目して欲しい。
・
・
・
・
・
そう、alloc〜init があって retain カウントが増えているが、さらに retain 属性があるため、retain カウントが +1 され、合計では +2 になっている。これは assign 属性の aView は alloc〜init の +1 だけである。そのため、dealloc では rView は2回 release しないといけないのだ。というより、alloc〜init で生成する場合は retain しちゃダメですな。この場合は assign 属性にすべきではないかな。
あー、ハマって勉強になった。
はてなブログ使ってみた
はてなダイアリーとの住み分けがよくわからないけど。
iOS の View-based Application テンプレートから OpenGL 使ってみる
http://katsuren.hatenablog.com/
USBストレージまたはSDカードにインストール出来ませんでした。
最近AndroidでデフォルトSDカードにインストールされるアプリが、
「USBストレージまたはSDカードにインストール出来ませんでした。」
とかいってインストールどころか更新もできなかったので調べてみた。
価格.com - 『SDカードにアプリをインストール出来ない現象』 シャープ IS05 au のクチコミ掲示板
このへんに書いてあった。
・PCとIS05を接続してSDカード内の以下のファイルを削除する。
.android_secure/smdl2tmp1.asec
これで無事インストール/更新出来るようになった。
Objective-C はじめました
なんか良くわからないけど、iPadのアプリを作らなきゃいけないかもしれないと思ったので。
まず手始めにデバッグできなきゃ話にならないので、そのへんのことやってみた。
iOS のアプリでは NSLog っていう関数があるらしい。そいつでデバッグログを出力する。んで、Objective-C は C の仕様を継承しているらしいので、ifdef なんかでデバッグビルドとリリースビルドでログ出力を制御したりするといい感じ。
デバッグ時だけログを出力させたい - 日々是笑心
このへんを参考にした。
NSLog はこんな感じで使う。
NSLog(@"unko"); NSLog(@"%d", 100);
んで、いざこんなふうにNSStringを渡してみると、ランタイムエラーで落ちる。
なんでじゃー!!!
NSString *str = @"unko";
NSLog(str);
この時点でポインタとかよくしらないボクは、きっとお作法的な何かだろうなと思ったりしてた。
ネット上のエロい人達のナレッジを拝借すると、みんなこんな感じに書いてるみたいだ。
NSString *str = @"unko"; NSLog(@"%@", str);
なるほど、sprintf っぽい書き方しなきゃだめなのね。
・・・でもまてよ、@"%@" は文字列でしょ?リテラルと変数ってなんか違うのん?
んー・・・。その文字列を変数に格納してみる。
NSString *fmt = @"%@"; NSString *str = @"unko"; NSLog(fmt, str);
とおったー!
でもなんで?w
ちょっと書いてみる
NSString *str = @"unko"; NSLog(str, @"");
これが通る。えー・・・
NSString *str = @"unko";
NSLog(str, nil);
そして、これも通る。えー・・・・・・・。
どうやら、第一引数がリテラルじゃない場合は、フォーマットを探しちゃうらしい。んで、なんでもいいからとりあえず第二引数をとると、なんか通る。なんだかなー。ちょっと気持ち悪い。
C++でcatを書いてみた
書いてみた。
こんな感じかな。
#include <iostream> int main(int argc, char *argv[]) { // もし第一引数が指定されなかったら終了 if (argc <= 1) { std::cout << "ファイルを指定してください" << endl; return 1; } // ファイルが見つからなかったら終了 FILE *fp; if ((fp = fopen(argv[1], "r") == NULL) { std::cout << "ファイルが見つかりませんでした" << endl; return 1; } // 読み取って出力 char buff[256] = ""; int i = 0; while (fgets(buff, 256, fp) != NULL) { std::cout << buff; i++; } fclose(fp); return 0; }
これを main.cpp なんかにかいて、
g++ -o cat main.cpp
とかってやればcatモドキができる。
Cはよくわからないのでいろんなものを調べたモノをコピペしてるんだけど、Cって文字列の扱いが難しい。このソースだと、一行に256文字以上あると、おかしくなるのかなー。ASとかJSだと動的に配列の上限変えられるから楽だよね。
MySQL の DATETIME 型と TIMESTAMP 型
DATETIME 型も TIMESTAMP 型もなんだかリファレンス見る限りはほとんど差がないように見える。
MySQL :: MySQL 5.6 リファレンスマニュアル :: 11.3.1 DATE、DATETIME、および TIMESTAMP 型
ここで言及されてるのは日付の範囲あたりで違いがあるっぽいってこと。AS で言うと Number と int の違いみたいな。
だけど、そんなんじゃなくて、TIMESTAMP はどうやら値指定しなきゃ勝手に現在時刻で更新され、DATETIME はそんなことがないらしい。
※ただし、テーブルに TIMESTAMP が複数ある場合は最初に定義されている TIMESTAMP のみらしい。
なぜか TIMESTAMP 型が勝手に更新されてハマった。