WEBアプリケーション研究室 開発ノート TOP

WEBアプリケーション研究室 開発ノート iPhone開発

スポンサーサイト

-------- --:--

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

    このエントリーをはてなブックマークに追加

某フライドチキンの話ではありません。CocoaにはKeyValueCoding(KVC)という機能がついていて、オブジェクトのプロパティを変更したり、その変更を監視する特別なメソッドが用意されてます。


#import

@interface HogeObject : NSObject {
@private
BOOL isChanged;
NSString *name;
}

-(id)init;
-(void)changeName: (NSString *) value;
-(NSString *)name;
-(BOOL) isChanged;
@end
+ (BOOL)automaticallyNotifiesObserversForKey:で監視するKey名を設定します。

#import "HogeObject.h"

@implementation HogeObject
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString*)key
{
if ([key isEqualToString:@"isChanged"])
{
return YES;
}

return [super automaticallyNotifiesObserversForKey:key];
}

-(id)init
{
if (self = [super init])
{
isChanged = NO;
name = @"hoge";
}

return self;
}

-(void)changeName: (NSString *) value
{
name = value;
[self setValue:[NSNumber numberWithBool:YES] forKey:@"isChanged"];
}

-(BOOL)isChanged
{
return isChanged;
}

-(NSString *)name
{
return name;
}

-(void)dealloc
{
[name release];
[super dealloc];
}
@end


HogeObject *test = [[HogeObject alloc] init];
//オブザーバーの登録
[test addObserver:self
forKeyPath:@"isChanged"
options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
context:nil
];
NSLog(@"%@:%@", [test name], [test isChanged] ? @"YES" : @"NO");

//変更してみる
[test changeName:@"oooo"];
NSLog(@"%@:%@", [test name], [test isChanged] ? @"YES" : @"NO");

//プライベートでも変更可能。changeNameの呼び出しではないのでオブザーバーは呼ばれない
[test setValue:@"mumu" forKey:@"name"];
NSLog(@"%@:%@", [test name], [test isChanged] ? @"YES" : @"NO");

//これは当然エラー
//test->name = @"gogogo";
ここではオブザーバーにselfを指定してるのでこのコードと同じクラスに以下のメソッドを実装します。これは必須です。

- (void)observeValueForKeyPath:(NSString*)keyPath
ofObject:(id)object
change:(NSDictionary*)change
context:(void*)context
{
NSLog(@"%@", change);
}
isChangedの値が変更されるとこのメソッドが呼ばれます。changeはどのように変更されたかの値がしまわれます。オブザーバー登録時にoptionsに渡したenumによってどんなデータが渡されるか設定できます。

    このエントリーをはてなブックマークに追加

UITableViewセルをクリックして、UINavigationControllerに新しい編集用のTableViewをpush、編集後保存ボタンをイベントにして、元のリストの一覧を読み直すというのをやってたのですが、とある本によると以下のようにやっていました。


[self.navigationController popViewControllerAnimated:YES];

NSArray *allControllers = self.navigationController.viewControllers;
UITableViewController *parent = [allControllers lastObject];
[parent.tableView reloadData];
これだとpopViewControllerAnimated:YESが実行された時点で、self.navigationControllerはnullになります。Objective-cではnullにメッセージを送ることは許されてるので、エラーにならず、reloadが行われないという状態になります。

正確には

NSArray *allControllers = self.navigationController.viewControllers;
NSInteger target = [allControllers count] - 2;
UITableViewController *parent = [allControllers objectAtIndex:target];
[parent.tableView reloadData];
[self.navigationController popViewControllerAnimated:YES];
自分の一個前の物を取得するので-2・・・気持ち悪いですね。現在のViewにポインタを持たせて呼び出した方がいいかも。ただポインタといえども微々たるメモリは消費すると思うので、iPhoneではこうした方がいいのかな・・・

    このエントリーをはてなブックマークに追加

NSArrayで例えば3番目の要素を5番目に移動したいとき、ちょっと気をつけなかればならないことがあります。


NSUInteger fromRow = 3;
NSUInteger toRow = 5;

id object = [[list objectAtIndex:fromRow] retain];
[list removeObjectAtIndex:fromRow];
[list insertObject:object atIndex:toRow];
[object release];
いったん取り出して、新しい場所に挿入すればいいのですが、removeObjectAtIndexした時点でNSArrayは対象要素をreleaseしますのでretainカウントが0になる可能性があります。retainしておかなければなりません。

    このエントリーをはてなブックマークに追加


//selectedListはNSMutableArray
for(NSInteger row as selectedList)
{
//ここでなにか
}
selector element does not have a valid object typeというエラーが発生。考えてみれば当然なんですけどNSArrayはObjectしか入れられません。こういう時はラッパークラスのNSNumberでラッパーしてNSArrayにいれてやります。

//selectedListはNSMutableArray
for(NSNumber *row as selectedList)
{
//ここでなにか
}

    このエントリーをはてなブックマークに追加

Viewをnibファイルから読み込んで表示するとき様々なタイミング初期化メソッドが呼ばれます。


#import "ViewBaseViewController.h"

@implementation ViewBaseViewController
@synthesize label;

- (void)loadView {
[super loadView];
NSLog(@"loadView");
}

- (void) viewWillAppear:(BOOL)animated
{
NSLog(@"viewWillAppear");
}

- (void) viewDidAppear:(BOOL)animated
{
NSLog(@"viewDidAppear");
}

- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"viewDidLoad");
}

- (void)dealloc {
[label release];
[super dealloc];
}

@end
実行すると

ViewBase[12262:207] loadView
ViewBase[12262:207] viewDidLoad
ViewBase[12262:207] viewWillAppear
ViewBase[12262:207] viewDidAppear
こんな順番で呼ばれます。気をつけなかればいけないのはloadViewとviewDidLoadはnibが最初に読み込まれる時だけ呼ばれます。Viewを切り替えて使うようなアプリケーションで切り替え時にUIの値を変更したい時はviewWillAppearでやらなければなりません。

それと、もう一点、UIの値を変更するタイミングですが、nibが読み込まれるまで変更はできません。例えばアプリケーションのデリゲートメソッドapplicationDidFinishLaunchingで

- (void)applicationDidFinishLaunching:(UIApplication *)application {
viewController.label.text = @"foo bar";
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
こんな風にやっても、nibを読み込む前なのでOutletが接続されていません。このタイミングで変更しても反映されません。javascriptで対象HTML要素が読み込まれる前に変更を試みるのとにてる感じです。

    このエントリーをはてなブックマークに追加
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。