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を使う事になります。データベースがOracleSQLiteなど、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サービスなどでの一覧表示、登録・更新画面など、全てのレコードが必要ない場合にはこちらを使います。