Study CoreData 12 ~ Simple & Safety ~

今日も始まりました『Study CoreData』。

このところ、あまりふざけたトコロがなく
何だかよくあるチュートリアル的な感じになってしまっている感はありますが、
(むしろ読みやすい!?)
スキあらばくだらない事もちょいちょい入れていきたいなぁと思っております(^〜^;)
 
 
それとこのエントリとは全くカンケーないんですが、
このブログを書く時に僕は文字のサイズや色を変えたり、枠付きのテキストを入れたりする事が多いんですが、
これって手打ちでタグを入れていくとそれだけでかなりの時間が取られちゃうんですよね。
(ただでさえ書くのに時間がかかる方なので…)

で、[キーワード+スペース]で定型文に変換してくれる『Kissphease』ってツール(ちょっと古いです)を使わせて頂いてます。
(例えば[bmojil+スペース]
<span style=”color:#000000;font-size:1.1em;”><strong></strong></span>に変換とか)

これかなり便利なので、定型文変換ってXcodeでのコーディングにも使えるんぢゃないかなぁ?なんて思ってます。

興味のある方はこちらを。
Keywordを登録しておくことにより定型文を差し込むことのできるPreferencePane『Kissphrase』
– Macの手書き説明書

 
 
…ってな感じで今日も始めちゃいますかい。

注意:投稿者自身もCoraDataについて勉強中のため、このシリーズには誤りが含まれている可能性があります。もし、間違いに気付かれた方はコメント欄もしくはtwitterなどでご指摘いただけると幸いです<(_ _)>
また、開発環境はXcode3.2.3 iPhone SDK 4です。実機でのテストなどは自己責任でお願いいたします。

 
 
前回は、RootViewの表示関係をやりましたので
今回は編集/新規作成用Viewを作っていきたいと思います。

ちょっとしばらくはビルドして動作や表示を確認できませんが
我慢してやってくださいまし<(_ _)>
 
 

1. 継承とサブクラスを考える

[Study CoreData 10]で、編集/新規作成用のViewControllerとして3つのクラスを用意してもらったと思います。
その時に[EditValueViewController]には、
残り2つのコントローラのための抽象クラス的な役割をさせる
ということを書いたと思います。

つまり親クラスを作るってコトになります。
なので、まずはその構成を考えてみましょう。
[EditableViewController]と[SelectableViewController]の両方で共通で使えるものを決めるってワケです。
 
 
そのために『TodoCore』でやってきた事が参考になりそうなので、
TodoCoreプロジェクトの[EditableViewController]も開いてください。

それと、参考用の完成イメージはこんな感じでしたね。

 
 
まず、[typedef enum]で宣言していた[EditType]ですが、
これはちょっといじれば使えそうですね。

次にデリゲートプロトコルですが、
まだRootViewでの保存の方法などを決めていないので、とりあえず保留にしときます。

その他に必須になりそうなのは、

・NSManagedObjectのプロパティ
・UITextFieldのプロパティ
・EditTypeを保持するプロパティ
・[_isFirstEditing]フラグ
・専用の初期化メソッド

他にも出てきそうですが、今のところこんな感じですかね。
 
 
…で、こんな感じで行っちゃってみましょうか?
(EditValueController.h)

今回は、各ViewControllerが2種類のViewを管理する事になるので、初期化メソッドの引数はコントローラではなく[Task *]に変更しました。
 
 
そしたら[EditValueController.m]にうつりましょう。
まず[Task.h]のインポートと、いつものようにプロパティ宣言したものをsythesizeしといてくださいね。

まずは初期化メソッドはこんな感じに。

これも特に難しいところはないですが、ハイライトされているところがポイントです!

共通の初期化メソッドを作るのは良いのですが、
[EditableView]と[SelectableView]ではテーブルビューのスタイルが違うため、[super]に送る前にEditTypeによってスタイルを判定させてみました。

これで、どちらのコントローラでもオーバーライドしなくてよくなります。
 
 
そしたら[cellForRowAtIndexPath:]も共通にしたいところですね〜。
ですが、ここでもセルのスタイルを分けなくてはいけないってコトに気付きました。

こういった処理を毎回if/else文で分けるってのは、
『ちょっとどうなのかなぁ??』って思いません?
 
 

2. 構造体にひとまとめ

…で、こんな時にアレが使えるのかも?って思って、こんなもんをヘッダファイルに追加してみました。
(ついでに入れ忘れていた<UITextFieldDelegate>も追加してます。^^;)

正直に白状すると、コレが構造体(C言語)というものらしいのですがよくわかっておりませぬ…<(_ _;)>

調べてみると、

「構造体」は幾つかの異なる型のデータをまとめて 1つのデータ型として扱うもの。

とのこと。

使ってみて便利だったので、このプロジェクトでも使ってみます。

説明すると…

1. [_EditingInfo]という構造体を作る。
2. メンバ(中に入っている変数)の表記は、[データ型] [変数名] : [最大値(?)]
3. 各メンバの説明
  ・cellStyle:セルのスタイル
  ・canMaltipleSelect:タグ選択Viewの時のみYES(1)
  ・showKeybord:ビューが表示された時にキーボードを表示するかどうか
4. これを使って初期化時にすべてのメンバに値を設定する。

こんな感じにしてます。
(実際は、int型で各変数を宣言するのと大きな違いはないのかも?)

そしたら、さっきの初期化メソッドにこの構造体への値の代入を追加します。

で、セルの構成へ。

セクションの数は“1”を返しときます。

[numberOfRowsInSection:]は各ViewController側で設定するため、ここでは削除しときました。

[cellForRowAtIndexPath:]では、セルスタイルだけを設定しておいて、各ViewController側では[configureCell: atIndexPath:]をオーバーライドする事にします。
 
 
そこまでできたら[dealloc]にRetain属性のプロパティの解放を追加して、
[EditableViewController.h]と[SelectableViewController.h]でこの[EditValueController.h]をインポートして、親クラスもEditValueControllerに変更
そして、一旦[RootViewController]へ移動しましょう。

 
 

3. NSManagedObjectの受け渡し2.0

ここで一度 原点に帰って
『NSManagedObjectContextを安全に使う方法』
ってのを考えてみたいと思います。

ではここでも、旧プロジェクト[TodoCore]の新規Event作成時のメソッドを参考にしてみましょう。
ってことでRootViewControllerを開いたら
[showModalAddTitleView][didSelectRowAtIndexPath:]の実装を見てみてください。

[TodoCore]ではManagedObjectの受け渡しは下のような感じにしてましたね。

1. 新規作成時には、[fetchResultsController]内のコンテキストへインサートした新しいManagedObjectを[addViewController]へ渡す。

2. 詳細編集時には、[fetchResultsController]からManagedObjectを取り出して[detailViewController]へ渡す。

これをコンテキストに注目してみて見ると、どちらも[fetchResultsController]内のコンテキスト。
かつ[fetchResultsController]の生成時に渡されていたのは
[self.managedObjectContext]です。

ってことはつまり、[AppDelegate]で生成された大もとのコンテキストを[ManagedObject]と一緒に受け渡ししていたワケですね。
 
 
ですが、よくよく考えてみると、
この『CoreDataのキモ』とも言えるメインのコンテキストをあっちへ渡したりこっちへ渡したり、保存したりしなかったり…ってのは、ちょいと危険なかをりがしやしませんかぃ?ダンナさま?

ってなワケで、Appleのサンプルコード”CoreDataBooks”などを参考に調べてみるとより安心設計なやり方がありました。
その答えとは…
 
 
『別のコンテキストを用意し、そのコンテキストにある/または作成したManagedObjectを受け渡す。』

なるほど!
これで大もとのコンテキストには手をつけずにデータを編集したりできるんですね。
 
 
…で、保存/キャンセルする時はどうすれば…?

『キャンセル時は新しいコンテキストを削除するだけ』

『保存時には、新しいコンテキストを保存して大もとのコンテキストと合成する。』
 
 
どうでしょう?
こいつぁ〜かなり安全な感じですね!
 
 

4. 新showModalAddTitleView実装!

では、早速新しい実装をしていきましょう。

まず、[RootViewController.h]に新しいコンテキストのためのプロパティなどを追加します。

※ハイライトされたところが追加箇所です

 
次に実装ファイルの[insertNewObject]を削除し、改良した[showModalAddTitleView]を実装します。

新規コンテキストを生成する以外は、今までとおなじ感じですね。
注意点は、[AppDelegate]でNSManagedObjectContextを作っていた時と同じように、新規コンテキストにもPersistentStoreCordinaterをセットしなければいけないってコトくらいです。
 
 

5. 続・新didSelectRowAtIndexPath実装。

[didSelectRowAtIndexPath]ではDetailViewControllerを初期化する必要があるので、一旦[DetailViewController]に移動して、初期化メソッドを用意しておきましょう。

とりあえず、ここは[TodoCore]とほぼ同じ感じで良いと思うのでこんな感じに。

(DetailViewController.h)
(DetailViewController.m)

(※スクロールは不可にしました。)

そこまでやったら[RootViewController]に戻りましょう。
 
 
実は、前述した『CoreDataBooks』の[didSelectRowAtIndexPath:]では、『TodoCore』とおんなじ受け渡しをしています。
ですが、やはりこちらもより安全にしたいので
『dotodo』ではこのメソッドでも別のコンテキストを使って受け渡したいと思います。
 
でも、セルが選択された時には
既にあるManagedObjectを編集することになるのですが、
そのManagedObjectのコンテキストは当然“大もとのコンテキスト”なワケです。

実はこういう場合、
元からあるManagedObjectと全く同じオブジェクトを
新規コンテキストから取得する方法

があるんです!
 
 
それがこちら。

まず、いつものようにfetchedResultsControllerから選択されたタスク(masterTask)を取り出します。

そしたら、そのmasterTaskのIDを取得

そして、そのIDを新規コンテキストに

これで、両方のコンテキストに同じTaskオブジェクトがある状態になります。
なので、あとは今まで通り使って保存/削除すればいいだけです。
 
 

6. ちょっとしたリファクタリング

ここでちょいとリファクタリングしたいと思います。

ですが、以前紹介したXcodeの機能の”リファクタリング”とは違います。

ここで言うリファクタリングは『コードを改善する』ってコトです。
 
 
先程実装した2つのメソッドの『新規コンテキストの作成』部分はほぼ同じですよね。
違うのは“NSManagedObjectIDを取得して…”という部分くらいです。

なので、新規コンテキストを生成してTaskを返すメソッドを別に作って、それを両方のメソッドから利用できるようにしたいと思います。
 
 
で、こんな感じにしてみました。

引数に”masterTask”を渡すようにして、nilなら新規に作成するって感じです。

そしたら、先程のふたつのメソッドをこんな感じに。

いやぁ〜♬
ずいぶんスッキリしましたねぇ。
眺めがいいです!
 
 
そしたらお次ぎは保存の処理を…
 
 
…と行きたいところですが、またまた長くなりすぎちゃったので
今日はココまで。
 
なかなかビルドできるようにならないのがもどかしいですが、
きっとこの後の初ビルド時には、かなりの部分が出来上がってるはずです!

なので、また明日もガッツリいきましょ〜!
 
 
んぢゃ、ばいちゃっ。

広告

Study CoreData 12 ~ Simple & Safety ~」への3件のフィードバック

  1. ピンバック: [朝刊] Skype がバックグラウンド通話可能に。課金プラン中止!ずっと無料!すげぇぞ!

  2. いつも参考にさせて頂いています。
    少し気になったのですが「コンテキスト」と「コンテクスト」は同じものですよね。
    表記は統一するべきだと思うのですが如何でしょう?初学者は混乱してしまうと思います。

    • コメントありがとうございます!

      どちらもcontextの日本語読みとして使われている言葉ですが、確かに統一した方が良いですよね。
      (指摘してもらうまで混じって使っていることに気づきませんでした^^;)

      とりあえずこの記事内では”コンテキスト”に統一して修正しましたが、なにせもう2年近く前のエントリになるのでご容赦くださいませ
      (初心者の方もこのコメントを読んでもらえれば分かるということで。)

      こんごともよろしくお願いします。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中