2013年11月30日

Xcodeのgitでファイルを前のバージョンに戻す

Xcodeで編集をしていて、コミットしたあとでファイルをコミット前の状態に戻したいことがある。

$ git revert -n HEAD


に相当するような処理だ。今回はそれそのものは見つけられなかったのだが、前のバージョンからの変更点を1個づつ指定して元に戻す方法を書いておく。

まず、元に戻したいファイルを選択してエディタに表示する。そしてバージョンエディタを選択して右側のビューに別のバージョンを表示できるようにする。編集中のファイルであれば、最後にコミットしたバージョンを表示して編集内容を見ることができるのだが、今回はコミット後なので左右のエディタには同じものが表示されている。

ここでタイムラインを表示する。これは二つのエディタの間の下部にある時計アイコンをクリックすることでバージョン履歴を表示させる。デフォルトでは左がlocal、右がbaseをさしている。履歴上のバーをクリックすることで右側のエディタの表示をより古いバージョンのものに置き換えることができる。今回は直前のものなのでひとつ前のバーをクリックする。マウスをロールオーバーするとコミットのコメントが表示されるので目安になる。
xcode_git_1.png

バーをクリックするとそのバージョンが右のエディタに表示されるので、左右のエディタでなんらかの違いがあるはずだ。これは右端に赤いバーで表示されている。その場所までスクロールさせてみると変更点がはっきりとわかるだろう。

もう一度、中間の下部にある時計アイコンをクリックしてタイムラインを消す。すると相違点の番号が表示されたボタンが中間の領域に表示される。これをクリックするとDiscard Changeというメニューが表示されるのでこれをクリックすると変更が前のものに戻される。
xcode_git_2.png

これを変更点の数だけ繰り返すと右側のバージョンに戻すことが可能。

一括してできないけど、逆に言うと複数の修正がある場合にはひとつひとつ選択して戻すか、戻さないか判断できるということ。




ラベル:Xcode git
posted by 永遠製作所 at 22:55| 東京 ☀| Comment(0) | TrackBack(0) | Cocoa | このブログの読者になる | 更新情報をチェックする

2013年07月25日

NSTableViewで行選択をしたときに呼ばれるデリゲートメソッド

NSTableViewで行選択をしたときに呼ばれるデリゲートメソッドは以下の4つ。


- (BOOL)selectionShouldChangeInTableView:(NSTableView *)aTableView
- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(NSInteger)rowIndex
- (void)tableViewSelectionIsChanging:(NSNotification *)aNotification
- (void)tableViewSelectionDidChange:(NSNotification *)aNotification


基本的には上記の順に呼ばれ、このうち前の2つが選択変更をしてもよいかどうか判定するメソッドでどちらか一方でもNOを返せば、それ以後のメソッドは呼ばれない。後の2つは選択実行時に呼ばれ、変更を取りやめることはできない。

上記メソッドが下記それぞれの状況でどのように呼ばれるかを確認する。

(A)最初の許可メソッドで不許可(NO)を返す場合

-selectionShouldChangeInTableView:
がNOを返す時、なぜかこのメソッドが2回呼ばれる。

データのない行をクリックするなどして全非選択(empty)にする場合には1回だけ。

他のメソッドは呼ばれない。

(B)最初の許可メソッドが許可(YES)を返すが、二番目の許可メソッドは不許可(NO)を返す場合

- selectionShouldChangeInTableView:
- tableView:shouldSelectRow:
- selectionShouldChangeInTableView:
- tableView:shouldSelectRow:

とやはり2回づつ呼ばれている。以後のメソッドは同じく呼ばれない。

(C)許可メソッドが許可(YES)を返す場合

2つの許可メソッドが共にYES,YESを返す場合には

- selectionShouldChangeInTableView:
- tableView:shouldSelectRow:
- tableViewSelectionIsChanging:
- tableViewSelectionDidChange:

が順に呼ばれる。

(D)追加行選択の場合

複数行選択(multiple)を認めている場合、二つ目以降の行を選択しようとすると以下の順に呼ばれる。

- selectionShouldChangeInTableView:
- tableView:shouldSelectRow:
- tableView:shouldSelectRow:

- tableViewSelectionIsChanging:
- tableViewSelectionDidChange:

tableView:shouldSelectRow は、選択済みの行も新たに追加選択した行も含めてすべての行について呼ばれることになる。

(E)追加行の許可メソッドが不許可(NO)を返す場合

新たに追加した行がすべてNOを返す場合には以下のようになる。

- selectionShouldChangeInTableView:
- tableView:shouldSelectRow:
- tableView:shouldSelectRow:


tableView:shouldSelectRowは全ての行について呼ばれるが、追加選択される行がなければ以後のメソッドは呼ばれない。

(F)選択なし(empty)になるとき

選択状態から選択行がなくなる場合には以下。

- selectionShouldChangeInTableView:
- tableViewSelectionIsChanging:
- tableViewSelectionDidChange:

個別行の選択許可を求めるメソッドが呼ばれない。

(G)一気に複数行選択した場合

複数行を(SelectAllなどで)同時にまとめて選択した場合には以下。

- selectionShouldChangeInTableView:
- tableView:shouldSelectRow:
- tableView:shouldSelectRow:

- tableViewSelectionDidChange:


tableView:shouldSelectRowは選択しようとした行数分だけ呼ばれるのだが、..IsChanging:が呼ばれなくなる。

(H)複数選択中に選択解除する行がある時

- selectionShouldChangeInTableView:
- tableViewSelectionIsChanging:
- tableViewSelectionDidChange:

tableView:shouldSelectRowはもちろん呼ばれないが、..IsChanging:や..DidChanged:は呼ばれる。

tableView:shouldSelectRowは選択しようとした行数分だけ呼ばれるのだが、..IsChanging:が呼ばれなくなる。

(I)一気に複数行選択解除した場合

Gの逆。

- selectionShouldChangeInTableView:
- tableViewSelectionIsChanging:
- tableViewSelectionDidChange:


(*)データ

ノーティフィケイションメソッドはobjectがNSTableViewになっているが、userInfoには何もセットされていない。

また、 NSTableView -selectedRowIndexes はshould..では選択される前の状態で選択された行を含んだ選択行番号の組は..IsChnaging:以降で取得できるようになる。

つまり、selectionShouldChangeInTableView:ではどの行が選択されるのかわからない状態でYES/NOを判断する必要がある。

まとめ



上記のことから各メソッドの役割を考察する。

- (BOOL)selectionShouldChangeInTableView:(NSTableView *)aTableView

あらたに選択する、選択を追加する、選択を(一部または全部)解除する場合など、とにかく選択状態が変わる時に「変えていいかどうか」確認するために呼ばれる。

とくに選択解除時にはどの行が解除されるか分からないことに加え、もうひとつのメソッドは呼ばれないのだから、複数行のうち選択解除してよい行と選択解除できない行が混在している場合でも選択解除不可としてNOを返さなければならない。

選択解除されようとしている行(クリックされた行)が分かればその行が選択解除OKかどうかで判断を変えることができるかもしれない。※10.5以降の注意を参照

- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(NSInteger)rowIndex

変更後に選択状態になる行すべてについて呼ばれる。既選択の行も呼ばれる。ここではじめて個別行について選択してよいかどうかの判定がかのう。選択解除される行については呼ばれない。

- (void)tableViewSelectionIsChanging:(NSNotification *)aNotification

選択状態を変更中(?)に呼ばれるメソッド。新たに選択した場合、解除した場合の両方で呼ばれるが複数行がまとめて選択される状況では呼ばれない。だが複数行がまとめて解除される場合には呼ばれる。謎が多いメソッド。

- (void)tableViewSelectionDidChange:(NSNotification *)aNotification

選択状態を変更した後に呼ばれる。あらたに選択された行がある場合、選択解除された行がある場合のいずれでも呼ばれる。


10.5以降の注意



10.5で以下のデリゲートメソッドが追加されている。


- (NSIndexSet *)tableView:(NSTableView *)tableView selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes


このメソッドがインプリメントされていると、tableView:shouldSelectRow: は呼ばれないでこちらのメソッドが呼ばれる。tableView:shouldSelectRow: を複数回呼ぶ変わりにこのメソッドが一回だけ呼ばれるようになっている。

そしてproposedSelectionIndexesに選択状態変更後に選択状態になる行番号の組がわたってくるのでこれと現在選択中の行-selectedRowIndexesを比較すれば選択解除されようとしている行が判明する。また選択される組み合わせでOK/NGを判定するような複雑な処理も可能になっている。
ラベル:Cocoa NSTableView
posted by 永遠製作所 at 15:52| 東京 🌁| Comment(0) | TrackBack(0) | Cocoa | このブログの読者になる | 更新情報をチェックする

2013年04月14日

Interface Builder上の深い階層のViewに直接アクセスする方法

今はXcodeに統合されているが、xib(nib)ファイルの編集をする時に使うエディタを今後も独立したアプリケーション時代同様にInterface Builderと呼びたい。

ちょっと複雑な画面の編集をしているとViewの上に別のViewを重ねて、さらにその上にViewを重ねることがある。特に、NSTableViewなんかはそれだけであってもNSScrollViewの下にNSTableView、NSTableColumn、NSTableCellなどと階層的になっているのでひとつ貼るだけで多重階層が簡単にできあがる。

この中の特定のViewの属性を変更したい場合に、選択するのが結構大変。最初にクリックすると一番外側のビューが選択され、そこでダブルクリックするとその内側のビューが選択される。そこでダブルクリックしてさらに内側と階層を降りて行って目的のビューを選択するのだが、うっかりクリックしすぎて目的のビューを通り越してしまうことがある。

そんなときには後戻りできないから、一旦全部の選択を解除してもう一度最初からやり直し。

これは非常に効率が悪い。

そこでやるのが、IBの左のドキュメントアウトライン表示。コントローラなどのオブジェクトやビューなどのオブジェクトを階層表示させることができる。これを使えば目的のビューを一発で選択できる。
ViewHierarchy2.png

でも、これを常時表示させるのは画面サイズが広くないといけないし、アウトラインの階層を開いていくのに時間がかかる。確かに選択間違いでやり直しはないけど。


目的のビューの上で「コントロール+シフト+クリック」すること。そうするとビューの階層をポップアップメニューの様に表示してくれるので一気にポインタの指すその位置にある好きなビューを指定することが可能。
ViewHierarchy1.png

この方法は随分昔からあるけど、Xcodeに統合されたときになくなったんじゃないかと心配だったけど、現在(Xcode 4.6.1)でもちゃんと使える。もちろんiOSアプリの場合でも同様。
ラベル:Xcode tips
posted by 永遠製作所 at 15:34| 東京 ☀| Comment(0) | TrackBack(0) | Cocoa | このブログの読者になる | 更新情報をチェックする

2013年03月14日

Xcode 4.6で外部フレームワークを追加する

昔開発したアプリケーションをメンテナンスする必要があったんだけど、それを開発していた頃のXcodeは3.2で実行できるマシンも結構古くて最新の便利で高速な環境に慣れている身としてはちょっと使いたくない。

軽微なバグ修正のみですぐ終わるのであれば、短時間なのでそのままの環境で開発した方が楽。だけど、ちょっと大規模な機能追加になりそうなので、環境は新しいほうがいい。

ということで、Xcode 4.6で開発することにする。単に新しい環境にプロジェクトを持って行き、プロジェクトをXcodeで開けばほぼOKだと思っていたが、ちょっと困った事があった。

このプロジェクトはOSに組み込まれているもの以外の外部フレームワークを使用している。だが、このフレームワークに含まれるヘッダファイルの読み込み個所でファイルが見つからないというエラーになってしまった。

前のXcodeでは普通にフレームワークをプロジェクトに追加するだけでヘッダファイルはそこを探索してくれたし、もちろんオブジェクトファイルもリンクしてくれる。その同じプロジェクトをビルドできないというのはどうにも納得がいなかい。

いったん、プロジェクトからフレームワークを削除して再度追加すると検索パスを認識してくれるかと思ったが、どうもうまくいかない。

色々調べた結果、OS組み込みのフレームワーク(/System/Library/Frameworks)以外のところにあるフレームワークは検索パスを設定しないといけないらしい。

ということでその設定方法。

続きを読む
ラベル:Xcode framework OpenBase
posted by 永遠製作所 at 21:08| 東京 ☀| Comment(0) | TrackBack(0) | Cocoa | このブログの読者になる | 更新情報をチェックする

2013年01月13日

[Cocoa][iOS]乱数の作り方

CocoaあるいはiOSで乱数を使いたい場合どうするか。

Cのライブラリ関数が3種類用意されている。伝統的に使われているrand()、これを改善したrandom()、そして最新のarc4random()だ。これはMac/iOSの両方で使える。

Macのみで使えるものとしてScreenSaver.frameworkのSSRandomIntBetween()、SSRandomFloatBetween()がある。

いずれも疑似乱数を生成する関数でrand()とrandom()はseedで初期化してそこからはじまる系列の疑似乱数値を順に取り出す。他は初期化が不要で呼ぶだけでよい。arc4random()はseedを指定することも可能。

それぞれ値の範囲に違いがあるがSSRandomFloatBetween()以外は整数値。適当な範囲にするために剰余を使う。小数値が欲しければ変換する必要がある。


rand() 0〜RAND_MAX
random() 0〜LONG_MAX
arc4random() 0〜ULONG_MAX


SSRandomIntBetween()、SSRandomFloatBetween()は値の範囲を引数で最小値〜最大値で指定する。

実行速度は下記順となる。
arc4random < random < rand

しかし以下で確認しているようにrandomとrandはほとんど速度に差がない。マニュアルを読み限りはrandomはrandの実行速度の3分の2になるように書かれているのだが。

SSRandomIntBetween()は内部でrandom()を使っているが、最大最小値の範囲に値を補正しているので若干遅くなっている。


time = 12 939 828(nsec) arc4random();
time = 6 012 601(nsec) random();
time = 6 720 593(nsec) rand();
time = 9 549 701(nsec) SSRandomIntBetween();




そしてCの各関数は乱数の分布がよく散らばっているものから順に
arc4random > random > rand
となっているはず。ただし確認テストでは実行回数が少ないせいか実行毎にばらつきかたが代わり顕著な優位性をみることができなかった(上位ビットでのばらつきと下位ビットでのばらつきの両方で見たがいずれも同じような結果)。

posted by 永遠製作所 at 16:13| 東京 ☀| Comment(0) | TrackBack(0) | Cocoa | このブログの読者になる | 更新情報をチェックする