xcodeで配列を保存する方法を教えてください

このQ&Aのポイント
  • xcode初心者の方が、ボタンを押すと日付を取得して配列に保存するアプリを作成していますが、MutableArray変数の宣言方法に問題があり、データの保存や読み込みがうまくいっていません。解決策を教えてください。
  • xcodeで配列を保存する方法についての質問です。日付を取得して配列に保存するアプリを作成していますが、MutableArray変数の宣言方法がうまくいっていないようです。どのように宣言すればよいか教えてください。
  • xcodeで配列を保存する方法を教えてください。日付を取得して配列に保存するアプリを作成していますが、MutableArray変数の宣言方法がうまくいっていないためデータの保存や読み込みができません。解決策を教えてください。
回答を見る
  • ベストアンサー

xcodeで配列を保存する方法を教えてください

初めまして。 うん十年ぶりにプログラミングに取り組んでいる、ccode初心者です。 ボタンを押すと、その時点の日付を取得して配列として保存するアプリを作っております。 ですが、変数の設定が悪いのか、MutableArray変数(_kitaku)をインスタンス変数で宣言すると、UserDefaultsで読み込んだ際に、読み込んだデータを取り込むことが出来ず、 ローカル変数で宣言すると、取得した日付を配列として保持することが出来ません。 おそらく変数の宣言の仕方かと思いますが、 どなたか、解決策を教えていただければ幸いです。 ちなみに、現在作成中のコードは下記の通りとなります。 よろしくお願いいたします。 ----- #import "OWBViewController.h" @interface OWBViewController () { //配列宣言 NSMutableArray *_kitaku; } //帰宅時間を取得するボタン @property (weak, nonatomic) IBOutlet UIButton *GetTime; //帰宅時間を取得するメソッド - (IBAction)GetTime:(id)sender; //時刻を保存するメソッド - (void)saveTime; @end @implementation OWBViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // 保存した時間を読み込む NSUserDefaults *Rireki = [NSUserDefaults standardUserDefaults]; NSMutableArray *_kitaku = [Rireki objectForKey:@"kitaku"]; // ブレークポイント NSLog(@"過去結果配列 %@",_kitaku); } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)saveTime { //保存 NSUserDefaults *Rireki = [NSUserDefaults standardUserDefaults]; [Rireki setObject:_kitaku forKey:@"kitaku"]; BOOL successful = [Rireki synchronize]; if (successful) { NSLog(@"%@", @"データの保存に成功しました。"); } } - (IBAction)GetTime:(id)sender { //NSMutableArray *_kitaku; NSDate *kitaku = [NSDate date]; if(!_kitaku){ _kitaku = [[NSMutableArray alloc] init]; } [_kitaku insertObject:kitaku atIndex:0]; NSLog(@"帰宅時間 %@", kitaku); NSLog(@"帰宅時間配列 %@", _kitaku); [self saveTime]; } @end

質問者が選んだベストアンサー

  • ベストアンサー
  • harawo
  • ベストアンサー率58% (3742/6450)
回答No.4

No.2のつづきです。 回答後、ちょっとしっくりこないところがあったので、テスト用のプログラムを改良して、もういちど確認したところ、mutableCopyしなくても、「objectForKey:」から読み出したArrayが、そのままMutableになることを確認しました。う~ん、いつのiOSのバージョンから、こうなったのだろう? NSUserDefaults Class Reference https://developer.apple.com/library/ios/documentation/cocoa/reference/foundation/Classes/NSUserDefaults_Class/Reference/Reference.html ここから引用: objectForKey: Returns the object associated with the first occurrence of the specified default. - (id)objectForKey:(NSString *)defaultName (中略) Special Considerations The returned object is immutable, even if the value you originally set was mutable. こう注記があります。 現在のコンパイラ(LLVM+Clang)では、Mutableになるからといって、将来のコンパイラでも同様にMutableになるという保証はありません。クラスリファレンスのこの注記が掲載される限り、それに従ったプログラムをすべきだと思います。

motanokwave
質問者

お礼

harawo様。 いろいろ確認頂き、本当に感謝しております。 自分のコードが、たまたまインサート可能になっていたこともよくわかりました。 今後もちゃんとアプリが動くように、規定にそったコードに直していきたいと思います。 この度は、私の拙い質問に、迅速にレスポンス頂き感謝しております。 他にもコメント頂いた方もいらっしゃり、皆様に感謝しておりますが、 まっさきに回答いただきましたharawoさんをベストアンサーにさせていただきました。 どうも有り難うございました。

その他の回答 (4)

回答No.5

No.3です。 すいません。私もてっきりimmutableになるとばっかり思いこんでいました。 というか、これ配列の読み込みなのにarrayForKeyじゃなくてobjectForKey を使っていますね。 objectForKeyを使うなら、 http://lab.dolice.net/blog/2013/12/30/objc-ns-user-defaults-and-ns-data/ にあるように、NSKeyedUnarchiverでNSDataに変換して読み書きする必要があると 思っていましたが、このあたりも最近は変わったようですね。 もしかすると内部で自動的にNSDataへの変換をするように実装が変わったのかも しれませんが、API仕様書でimmutableだと言っている以上、immutable扱いで 処理すべきですね。 それと、No.3で意味不明の3行コードがあるので削除すべきだと コメントしましたが、これってもしかしてNSUserDefaultsに まだ何も書きこまれていない時の初期化の意味でやっているんでしょうか? だとしたら、ここで初期化するのではなく、viewDidLoadで [Rireki objectForKey:@"kitaku"]; をした直後にやるべきだと思います。 または、NSUserDefaultsには、まだ何も書きこまれてない時のデフォルト値を 登録する機能がありますので、objectForKeyで読み込む前に、 [Rireki registerDefaults:@{@"kitaku":@[]}]; と書いておけば、まだ何も書きこまれていない場合、_kitakuに初期化された配列 が格納されるようになります。他にもたくさん設定値があっていろいろな項目の 初期値を決めないといけない時は、registerDefaultsでまとめて初期値を登録して おくと便利です。

motanokwave
質問者

お礼

再度のご指摘、本当に有り難うございます。 初期化の話も含めて、どのセクションに何を書くべきなのかも曖昧な状態ですので、さらに勉強していきたいと思います。 大変勉強になりました。 有り難うございました。

回答No.3

> 私のコードでは最後にご指摘いただいたmutableCopyのような処理は入れておりませんが、 > ちゃんと_kitaku配列はinsert可能になっております。 > その辺り、どうして問題なく出来るのかご教示いただければ、幸いです。 確かに、質問に書かれているコードはinsertでエラーになりません。 だって、 [_kitaku insertObject:kitaku atIndex:0]; のinsertの前に if(!_kitaku){ _kitaku = [[NSMutableArray alloc] init]; } という、変な3行のコードがあるじゃないですか。 No.1さんの最初の指摘にある通り、NSDefaults読み込み処理は インスタンス変数の_kitakuじゃなくて、ローカル変数の _kitakuに読み込んでいます。だからインスタンス変数の_kitaku はnilのままです。 このため、GetTimeメソッドが呼ばれた時、インスタンス変数の_kitaku がnilなので、上述の3行コードにより新しいNSMutableArrayを生成して _kitakuに格納しています。それに対してinsertObjectしているの だからエラーにはなりません。 でも、本当はこんなことをやりたいんじゃないでしょ? たぶん、最初insertObjectをした時に_kitakuがnilだというエラーが 出て、読み込んだはずの_kitakuがなぜnilなのか、その原因がよくわからず とりあえずエラーにさせないよう安直に上述の意味不明コードを 入れたのではないかと思います。 このコードのせいで自分で自分を罠に陥れているということです。 No.1さんの最初の指摘を修正した上で、この変な3行コードを 削除してください。 そうすれば、No.2さんの回答通り、Mutableでない配列にinsertは できないというエラーが出るはずです。 それを確認したら、No.1さんの後半の指摘通り修正してください。

motanokwave
質問者

お礼

Lchan0211b様。 的確なご指摘有り難うございます。 ご推察の通り、謎の3行はあまりよくわからずエラー回避のために入れていました。 皆様のご指摘ふまえ、あるべき姿に修正頑張ろうと思います。

  • harawo
  • ベストアンサー率58% (3742/6450)
回答No.2

> いただいたコメントの最後の部分でアドバイスいただいた内容ですが、私のコードでは最後にご指摘いただいたmutableCopyのような処理は入れておりませんが、ちゃんと_kitaku配列はinsert可能になっております。 じっさいに、NSUserDefaultsから読み出した、NS(Mutable)Arrayインスタンスに、要素をインサートしようとしたら、下記のエラーが出ました。 2014-09-02 19:45:51.300 DefaultsTest[2866:60b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray insertObject:atIndex:]: mutating method sent to immutable object' Immutableなオブジェクト(Array)にMutableなメソッドを送ったという、「期待どおりの」エラーメッセージです。 > その辺り、どうして問題なく出来るのかご教示いただければ、幸いです。 「問題なく出来る」プログラムを見せてください。そうでなければ、検討のしようがありません。

  • harawo
  • ベストアンサー率58% (3742/6450)
回答No.1

- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib.(みっともないから、このコメント文は削除しようぜ) // 保存した時間を読み込む NSUserDefaults *Rireki = [NSUserDefaults standardUserDefaults]; NSMutableArray *_kitaku = [Rireki objectForKey:@"kitaku"]; // ? _kitaku = [Rireki objectForKey:@"kitaku"]; // ブレークポイント NSLog(@"過去結果配列 %@",_kitaku); } インスタンス変数の_kitakuとはべつのNSMutableArray *_kitakuを作っちゃうと、まずいでしょう。 蛇足: NSUserDefaultsに、Mutableなオブジェクトは保存できません。保存して、読み出したら、Immutableなオブジェクトになっています。なので、読み出す都度、「mutableCopy」するか、「[NSMutableArray arrayWithArray: savedArray];」こんな処理が必要になるでしょう。

motanokwave
質問者

お礼

harawoさん、早速の回答ありがとうございました。 おかげ様で、問題解消しました。 まだ変数のことがよくわかっていないようで、もっと精進します。 本当にありがとうございました。

motanokwave
質問者

補足

harawoさま いただいたコメントの最後の部分でアドバイスいただいた内容ですが、私のコードでは最後にご指摘いただいたmutableCopyのような処理は入れておりませんが、ちゃんと_kitaku配列はinsert可能になっております。 その辺り、どうして問題なく出来るのかご教示いただければ、幸いです。 よろしくお願いいたします。

関連するQ&A

  • XcodeのObjective-Cについて

    大変初歩的な質問でお恥ずかしいのですが、最近Xcodeを初めて本で少しずつObjective-Cを勉強中の初心者です。 MainStoryboardでLabelをViewに配置した後、 「画面を表示する準備ができたときにコンソール画面に"こんにちは"と表示させるプログラムを追加」というものが載っているのですが、指示通り打ち込んでもエラーでうまくいきません。 本には「m.」ファイル上の「@implementation」の次の行に 01 - (void)viewDidLoad 02 { 03   [super viewDidLoad]; 04   NSLog(@"こんにちは"); 05 } とあり、その通りに打ち込んだのですが(以下Xcodeよりコピペ) @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; NSLog(@"こんにちは"); } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } となり、 NSLog(@"こんにちは"); } の次の行の - (void)viewDidLoad に赤い!マークのエラーが表示されてしまいます。 初心者のため訂正方法もわからずネット検索しても解決しなかったので、 大変申し訳ないのですが教えていただきたく思います。

  • xcodeについてご相談があります。

    objective-cを勉強中なのですが、分からない所があります。 (1)作成したリストを、ボタンを押すたびに一つずつランダムに選んでラベルに表示させる (2)同時に、選んだものはリストから削除していく (3)全て(下記の場合は5つ)選び終わったら、リストを完全に復活させる というメソッドを作りたいのですが、(3)の段階を上手く記述できません。 具体的には、if文の条件を上手く設定できません。 どなたかアドバイスを頂けないでしょうか。 以上、何卒宜しくお願い致します。 - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. _lists = [NSMutableArray arrayWithObjects:@"apple",@"bear",@"hand",@"foot",@"sun", nil]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (IBAction)btRandom:(id)sender { if (_lists > 0) { self.lbLabel.text = nil; // listからインデックス番号をランダムに選択 unsigned int erabu = arc4random_uniform(_lists.count); NSLog(@"1:%d", erabu); // listからランダムに選択したものを抜き出して文字データにする NSString *nukidasi = [_lists objectAtIndex:erabu]; NSLog(@"2:%@", nukidasi); // ラベルに出力する self.lbLabel.text = nukidasi; // listからランダムに選択して抜き出したもの(erabu)をlistから削除 [_lists removeObjectAtIndex:erabu]; NSLog(@"%@", _lists); }else if (_lists < 0){ _lists = [NSMutableArrayarrayWithObjects:@"apple",@"bear",@"hand",@"foot",@"sun", nil]; } }

  • IBAction内でのEXC_BAD_ACCES

    iPhone開発関係で質問です。 IBAction内でNSMutableDictionaryを参照しようとするとEXC_BAD_ACCESSとなってしまい、困っています。メモリ管理が甘いのでしょうか?? プログラムの概要は以下のとおりです。 問題をご存知の方、ご教授下さい。よろしくお願いします。 ---------- FirstViewController.h @interface FirstViewController : UIViewController { NSMutableDictionary *testDic; } @property (nonatomic , retain) NSMutableDictionary *testDic; - (IBAction) TestBtnTapped; @end ---------- FirstViewController.m #import "FirstViewController.h" @implementation FirstViewController @synthesize testDic; /* testDicの受け渡しがうまくいかない TestBtnTappedでEXC_BAD_ACCESS発生 IBActionのせい? */ - (IBAction) TestBtnTapped{ NSlog(@"%@" , testDic); } - (void)viewDidLoad { [super viewDidLoad]; testDic = あるNSMutableDictionaryデータ NSlog(@"%@" , testDic ); //ここでは表示される } - (void)dealloc { [super dealloc]; }

  • テーブルビューで配列が自動消滅することについて

    xcode4.1をつかってiphoneアプリを作成しています。 テーブルビューの画面遷移時に、 可変配列NSMutableArrayに plistを読み込ませたNSMutableDictionaryの一意のキーの中身を保存し、 更に選択した行番号で呼び出した配列の中身を別ビューに渡そうとすると失敗してしまいます。 ・・自動解放されてしまっていると思うのですが、 [[[NSMutableArray alloc] init] retain]; としてもうまくいきませんでした。 どうすればうまくデータを別ビューに渡せるでしょうか。 (plistの読み込みは必須です。。) すみませんがご教授いただけますでしょうか。。。 よろしくお願いいたします。 //-------- .h------ @interface DtlVCL : UITableViewController { //別のViewからデータ受け渡し用 NSString *rcvStr; //plist一時読み込み用 NSMutableDictionary *dic; NSMutableArray *ary; } .m------------ viewDidLoad------ //各変数初期化 ary = [[[NSMutableArray alloc] init] retain]; dic = [[[NSMutableDictionary alloc] init] retain]; id path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"xxxxxxxx.plist"]; if([[NSFileManager defaultManager] fileExistsAtPath:path] == YES){ dic = [NSMutableDictionary dictionaryWithContentsOfFile:path]; }else{ NSLog(@"Dtl :ファイル読み込みエラー"); } ary = [dic objectForKey:rcvStr];//rcvStrは前ビューから取得した文字列 ------------------- - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath------ ExpVCL *expvcl = [[ExpVCL alloc] initWithNibName:@"ExpVCL" bundle:nil]; //データの受け渡し expvcl.rcvNum = [indexPath row]; expvcl.rcvStr = [NSString stringWithFormat:@"%@",[ary objectAtIndex:[indexPath row]]]; [self.navigationController pushViewController:expvcl animated:YES]; [expvcl release];

  • iOSシュミレーターで表示されません

    iOSのプログラミングを始めたばかりの者です。 本を読み、下記の通りにプログラミングをしましたが、iOSシュミレータで全く表示されません。 どうしてなのか、お知恵を拝借出来ませんでしょうか? XCodeは、4.6.3で、MacBookは10.8.5です。 宜しくお願い申し上げます。 -------------------------------------------------------------------------------------------------------- #import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. NSLog(@"Hello,World!"); } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end

    • ベストアンサー
    • Mac
  • ios/バックグラウンドからループを呼び出したとき

    objective-cで、バックグラウンドに入ったときに、for文のあるメソッドを呼び出したところ、ループが終わると画面が黒くなってしまいます。落ちてはいないです。どうなっているんでしょうか? 以下が実際のソースです - (void)viewDidLoad { [super viewDidLoad]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:UIApplicationDidEnterBackgroundNotification object:nil]; } -(void)test{ for (int i; i < 5; i++) { NSLog(@"%d", i); [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0f]]; } }

  • Xcode4のエラー(IPhoneアプリ)

    はじめまして。 IPhoneのアプリ作成に挑戦しています。 Xcode4のバージョンは 4.5.1です。 【はじめてのXcode4プログラミング】という参考書でIPhoneのカメラアプリを作る章があり、写真ライブラリから選択した画像を表示できるとのことです。 しかし、XcodeでRun(実行)すると、ViewController.mの最後2行【UIImage *originalImage =[info objectForkey:UIImagePickerControllerOriginalImage];】が赤くなり、【’dismissModalViewControllerAnimated’is deprecated: first deprecated in iOS 6.0】というエラー表示がでます。 全体のコードは下記の通りです。 ネットで調べてみましたが、解決方法がわかりません。 先に進めず困っています。 どうか解決方法を教えてください。 よろしくお願い致します。 ●ViewController.h// // ViewController.h // CameraApp // #import @interface ViewController : UIViewController - (IBAction)pressCameraButton:(id)sender; @property (weak, nonatomic) IBOutlet UIImageView *imageView; @end ●ViewController.m // // ViewController.m // CameraApp // #import “ViewController.h” @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (IBAction)pressCameraButton:(id)sender { UIImagePickerController *picker = [[UIImagePickerController alloc] init]; picker.delegate = self; picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; [self presentModalViewController:picker animated:YES]; } - (void)imagePickerController:(UIImagePickerController*)Picker didFinishPickingMediaWithInfo:(NSDictionary*)info { [self dismissModalViewControllerAnimated:YES]; UIImage *originalImage = [info objectForkey:UIImagePickerControllerOriginalImage]; self.imageView.image = originalImage; } @end

  • iPhone開発について(変数)

    初歩的な質問ですが、お願い致します。 iPhoneでボタンを触っている時間を取得したいのですが 思い通りに動きません。 ソースを掻い摘んで引用致します。 #import "APPNAMEViewController.h" NSDate *startTap; - (IBAction)tapBtn_down{ startTap = [NSDate date]; } - (IBAction)tapBtn_up{ NSLog(@"%f",[startTap timeIntervalSinceNow]); } 上記のような感じで作成しております。 ボタンをアップしたときに、EXC_BAD_ACCESSのエラーになってしまいます。 基本的なところで、お恥ずかしいですがよろしくお願い致します。 また、タップ時間の計測でサンプルソースのあるページや もっと頭のいいやり方がありましたら、ご教授いただけますと幸いです。 よろしくお願い致します。

  • [objective-C]配列について

    はじめまして。 objective-C、cocos2dについての質問です。 iPhoneアプリを勉強しながら作り始めたのですが詰まってしまい、できればご助力をお願いしたいと思い質問させていただきました。 cocos2dというフレームワークを使用し簡単なRPGを作りたいと考えております。 そこでキャラクターのステータスを配列に格納し、どのシーンからも参照したいのですが、どうしてもうまくいきません。 別クラスでインスタンス変数を作り、それを利用して配列を作成する方法で実装しようとしており、実際のコードは以下の通りになります。 ---------------------------------- <Character.h> @interface Character : NSObject{ NSInteger characterStateAtk_; ・(Def、Hpなど) ・ } @property (nonatomic, assign) NSInteger characterStateAtk;  ・  ・ @end <Character.m> #import "Character.h" @implementation Character @synthesize characterStateAtk = characterStateAtk_;  ・  ・ @end キャラクターステータス生成部分 <CreateScene.h> @interface CreateScene : CCScene { NSMutableArray* characterStateArray_; } @property (nonatomic, assign) NSMutableArray *characterStateArray; @end <CreateScene.m> @interface CreateScene() - (void) pressReloadButton:(id)sender; - (void) createCharacterState; @end @implementation CreateScene @synthesize characterStateArray = characterStateArray_; - (void) dealloc{ [self.characterStateArray release]; [super dealloc]; } - (void) createCharacterState{ self.characterStateArray = [NSMutableArray array]; Character* priState = [[[Character alloc]init] autorelease]; priState.characterStateAtk = random() % 5 + 5; ・ ・ [self.characterStateArray addObject:priState]; } (その他の処理) - (void) pressReloadButton:(id)sender{ [self createCharacterState];  (ここでcharacterStateArrayを取得して表示したい) } ---------------------------------- 最初は、 NSInteger x; x = [[self.characterStateArray objectAtIndex:0]intValue]; で取得しようとしたのですがエラーとなり実行できませんでした。 (調べていくうちにobjective-Cでは配列の中身はオブジェクトしか使えないという事を知りました) プログラム中の(ここでcharacterStateArrayを取得して表示したい)の部分について、実装方法や考え方についてご助言をいただけないでしょうか? よろしくお願いいたします。

  • swift データの保存、読み込みについて

    swiftのデータ保存と読み込みについて学習しています。 saveボタンを押すとテキストフィールドの文字が保存され、switchボタンを押すと保存された文字列が表示され、文字列が表示されている場合はその文字列を消去するプログラムを書いたのですが、実行してみるとデータ保存がうまくいっていないようでした。 どこに問題があるのでしょうか。回答をお願いします。 以下ソースコードです import UIKit class ViewController: UIViewController { @IBOutlet weak var word1: UITextField! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } @IBAction func save1(sender: AnyObject) { let ud1a = NSUserDefaults.standardUserDefaults() ud1a.setObject(word1.text, forKey: "saveText1") } @IBAction func switch1(sender: AnyObject) { let ud1b = NSUserDefaults.standardUserDefaults() if(word1.text == nil){ var loadText1:String! = ud1b.stringForKey("saveText1") word1.text = loadText1 } else{ word1.text = nil } }