UIActivityIndicatorViewが動かない。
メソッドの中で、重い処理が合ったので、
MenuViewController.h
@interface MenuViewController : UIViewController { UIActivityIndicatorView *indicator; } -(void) shori; @end
MenuViewController.m
- (void)viewDidLoad { [super viewDidLoad]; indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; indicator.frame = CGRectMake(100.0, 100.0, 50.0, 50.0); indicator.center = self.view.center; [self.view addSubview:indicator]; } -(void) shori { [indicator startAnimating]; /* 重い処理 */ [indicator stopAnimating]; }
としたが、動かなかった。
google先生に聞いてみた所、同じスレッドで実行しちゃ駄目と言われる。
別スレッドで行うには
performSelectorInBackgroundを使えば良いらしいのだが、引数が一つしかとれない。
しかも、「本スレッドの中でなければUIは操作してはいけない。」との制限付き。
結局、startAnimatingと、stopAnimatingをバックグラウンドで行う事に。
-(void) shori { [self performSelectorInBackground:@selector(indicatorStart) withObject:nil]; /* 重い処理 */ [self performSelectorInBackground:@selector(indicatorStop) withObject:nil]; } -(void) indicatorStart { [indicator startAnimating]; } -(void) indicatorStop { [indicator stopAnimating]; }
Simulatorで加速度センサーを使う。
iPhoneのシミュレーターでは加速度センサーを使えない。
しかし、http://www.brianhpratt.net/cms/index.php?page=accsimを使うと、
シミュレーターで加速度センサーを使う事が出来るようになる。
まずは、AppStoreでaccelerometer-simulator(無料)
http://itunes.apple.com/jp/app/accelerometer-simulator/id336476721?mt=8
を取得。
次に、AccelerometerSimulation.hとAccelerometerSimulation.mを公式HPから取得し、
XCodeを立ち上げて、適当な場所に追加。
xxxxViewControllerに
#import "AccelerometerSimulation.h"
を追記。
シミュレーターを開始した後、
iphoneにてAccSimを立ち上げ、送信先(Mac)のIPアドレスを設定。
これで、Web経由で加速度センサーの情報を送って、
シミュレーター側で受け取ってくれる。
TableViewのreloadDataで落ちる。
Webからデータを取得してリストに表示させる機能を作っているときに、落ちたので記録。
NSArrayをproperty設定した後、再読み込み時に一旦からのテーブルを見せようと、
[searchResult release];
[self.tableView reloadData];
としたら落ちた。
[searchResult release]; searchResult = nil; [self.tableView reloadData];
で、解決。releaseした後すぐには空にならないのか?
NSFetchedResultsControllerでNSPredicateエラー
テンプレートのNavigation-Based Applicationで、
Use Coredataにチェックを入れて開始すると、
NSFetchedResultControllerを使ったアプリが出来る。
これを改造して使い方を勉強しようとfetchRequestにNSPredicateを使ってみた所、
条件を変更するとエラーが出力される現象に出会った。
Cocoaの日々様の
Cocoaの日々: -[NSFetchedResultsController performFetch:] でクラッシュ
にて解決。
キャッシュに持っている条件と違うからエラーが出ていたようだ。
元ソース
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Root"];
に対して、
キャッシュを使わない仕様にするか、(cacheNameにnilを指定)
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
条件を変える場合、キャッシュを消す仕様にするか、(deleteCacheWithNameで消す。)
[NSFetchedResultsController deleteCacheWithName:@"Root"]; NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Root"];
が有効手段のようだ。
キャッシュはアプリを閉じても残っているので、
アプリ内で条件を変えなくても、仕様変更などで条件を変えた場合、一旦削除しないとならない。
UIActionSheetを表示する。
UIViewControllerでボタンを押した時に、自分自身をdelegate先に指定してUIActionSheetを表示する。まずは、UIActionSheetのプロキシー(Javaで言う所のインターフェース)をUIViewControllerのヘッダーファイル(.h)に設定する。
//EditViewController.h @interface EditViewController : UIViewController <UIActionSheetDelegate>{ } - (IBAction) pushMenu:(id) sender; @end
次に、ボタンを押した時の処理で、UIActionSheetを作成し、delegate先を自分自身(self)に指定する。otherButtonを増やしたいときは、カンマ(,)で区切り追加する。otherButtonの最後はnilで終わるようにする事。
//EditViewController.m - (IBAction) pushMenu:(id) sender { UIActionSheet* sheet; sheet = [[UIActionSheet alloc] initWithTitle:@"Menu" delegate:self cancelButtonTitle:@"Cnacel" destructiveButtonTitle:@"Save" otherButtonTitles: @"Load", @"Settings", nil]; [sheet autorelease]; [sheet showInView:self.view]; }
最後に、UIActionSheetのボタンが押された時の処理を追加する。
//EditViewController.m - (void)actionSheet:(UIActionSheet*)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { NSLog(@"index:%d", buttonIndex); if (actionSheet.title == @"Menu") { switch(buttonIndex) { case 0: { //Save NSLog(@"Save"); break; } case 1: { //Load NSLog(@"Load"); break; } case 2: { //Settings NSLog(@"Settings"); break; } } } }
NSIntegerをintに変換する方法。
NSInteger nsi = 1; int i = nsi;
以上。
エラーで落ちるときは、NSIntegerのObjectが生成されていない事を疑う。
例1)
NSMutableArray *ma; NSInteger i = [ma count];
とか、
例2)
NSUserDefaults *userDafaults = [NSUserDefaults standardUserDefaults]; NSData *data = [userDafaults dataForKey:@"list"]; _ma = [NSKeyedUnarchiver unarchiveObjectWithData:data]; 別メッソドにて int i = [_ma count];
とすると、落ちる。
例1は、allocしていないのが原因。
NSMutableArray *ma = [ [NSMutableArray alloc] init];
例2は、retainしていないのが原因。
_ma = [ [NSKeyedUnarchiver unarchiveObjectWithData:data] retain];
とすれば、解決するだろう。
O/R マッピングについて。
メモリ上のデータと、データベースにしたときのデータの持ち方についてのお話です。
O/Rマッピングとは?
Object指向での開発で行う場合、メモリ上のデータはEntitiyと呼ばれ、Objectに格納されます。ですが、メモリ上で持てないデータの置き場所や、保存場所としてのデータベースに格納する場合の主流は、リレーショナルデータベース(Relational DB)になります。
データを集合的に木構造に持つObjectに対して、Relational DBは表形式でデータを保持し、キー項目でそれぞれの表を関連づけて管理しています。
この異なる構造の読み込み、書き出しをマッピングすることを、O/R Mappingと呼んでいます。
O/Rマッピングの実装
やり方的には、DBをObjectに合わせる方法と、ObjectをDBに合わせる方法があります。
DBをObjectに合わせる場合、木構造を保存出来るCacheなどのDBを使う事になります。データベースがOracleやSQLiteなど、Relational DBに制限されている場合は、この方法はとれません。(ORMappingの主旨からも少しずれているし・・・。)
ObjectをDBに合わせる方法の場合、テーブル用Objectに、レコード用Object(Structでも可)の配列を持たせます。
DBから取得するレコードについて
- 1テーブルにつき1テーブル用Objectを作成し、全てのレコードObjectに一括で取得する。
この場合、テーブル内の検索、ソートはメソッドでレコード分のループを回して行います。また、テーブル間のリレーショナルについても、ControllerとなるObjectを作成し、レコード分のループを回して行います。
レコードの読み書きについては、レコード用ObjectとDBのレコードが1対1の関係になり、行いやすくなります。
メリットは、ロードした後はメモリ上に配置されているので、大量の計算でも高速に処理を行う事ができることです。
デメリットは、実装が大変な事と、メモリをレコード分消費する事です。
DB全体を使った計算を行うC/Sアプリを作成する場合に使います。
- SQLで検索した情報(View)を1Objectとし、レコードObjectに取得する。
SQLで連結・検索し取得したレコードを保持することで、リレーショナル問題を解決します。
当然ながら取得しなかったレコードについては知る由はありません。
書き込み時は、読み込み時のObjectとは別に、対象となるテーブル用のレコードObjectを作成して行います。
Webサービスなどでの一覧表示、登録・更新画面など、全てのレコードが必要ない場合にはこちらを使います。