Month: May 2015

Swift:リカーシブルに動かしてみました。

フォルダの中に写真ファイルがあるかどうか調べるために、自分のメソッドを自分で呼び出して処理してみました。

すごく苦労していろいろ考えながらプログラムを書いては消す作業を繰り返していました。いわゆるリトライ、リトライ、リトライ。

このやり直しで、結局、500行くらい無駄に書いた末に、50行少々でやりたいことができました。やりたかったことは、フォルダの中から写真があるフォルダだけをツリー構造で表示し、クリックされたらその中にある写真のフォルダをサムネールに展開する機能です。

答えは、filepathの構造を解析してそれぞれの階層を親フォルダ番号(インデックス)にしてNSMutableArrayを活用するでした。

フォルダの中のフォルダの検索は、リカーシブルにすれば良さそうだったので、試しにそうしたらちゃんと動いているようで、ずっと深い階層に置いた写真のフォルダを探し当てることができました。あとで、本当にちゃんと動いているか、メモリリークにつながることはないか調べないといけません。

Swiftで再帰呼び出しがこんなに簡単にできるのなら、もっとほかのことに応用ができそうです。

基本機能が実現できたので、悶々としたフォルダの階層管理からようやく解放されそうです。

他方、Tree構造のサンプルプログラムはSwiftで動いているのですが、.xibからstoryboardに移行できていません。.xibがOKでstoryboardがダメな理由は、View Controllerと関連しているようですが対処方法がわかりません。もう一点わからないのは、アイコン表示ができていないことです。この2点が問題でソースコードのアップを保留しています。

Swift:配列で泣き(2)

ちゃんと勉強しなかったツケをようやく払いつつあります。

このサンプルを応用すれば、フォルダ管理が簡単にできることがわかりました。まだ最後まで行っていないので、「多分」がつきますけど。


特定のディレクトリを読みだしたときの状態。実際は数値でなく文字列になり、1個のみで複数の行は発生しない。
var wName:[NSArray] = []

wName.append([0, 1, 2, 3, 4, 5])
wName.append([6,7,8,9])
wName.append([1,1,1,1])

それぞれのフォルダの中身をディレクトリ名とセットで登録していく。インデックスはpathを分解して、base pathを0とした相対位置とする。wNameのほかに、写真が含まれるフォルダかどうか識別する列を追加した方がいいかもしれない(そうしない場合、wNameの先頭文字にその識別子を入れてしまうと、分解・合成が必要になる)。
var wparent:NSMutableArray = []
var wbase:String    = “/hk/Desktop”
wparent[0] = [wbase, wName[0]]
wparent[1] = [“Photo”, wName[1]]
wbase          = “abcdef”
wparent[2] = [wbase, wName[2]]
println(__LINE__,”parent格納結果は、wString=\(wparent)です。”)

クリックされたディレクトリは、base pathを0とした相対位置にあるディレクトリ名と一致する中身を持っている。2次元配列にしておけば、直接、フォルダ中のフォルダ名を参照できる。
println(__LINE__,”取り出し方 wparent[0]=\(wparent[0])”)
println(__LINE__,”取り出し方 wparent[0][0]=\(wparent[0][0])”)
println(__LINE__,”取り出し方 wparent[0][1]=\(wparent[0][1])”)
println(__LINE__,”取り出し方 wparent[1][1]=\(wparent[1][0])”)
println(__LINE__,”取り出し方 wparent[1][1]=\(wparent[1][1])”)

println(__LINE__,”配列数 wparent[0][0].count=\(wparent[0][1].count)”)
println(__LINE__,”配列数 wparent[1][1].count=\(wparent[1][1].count)”)

Swift:これまた目から鱗。文字列検索。

文字列の検索には、こんな方法があったのですね。

(変更 5/30/2015) 文字列検索は、次のメソッドの方が取り組みやすいです。

pDataの中にpTargetという文字列が入っているかどうか調べてその文字位置を返す。マイナスのときはNot found.
func CallSearchWords(pData:String, pTarget:String)-> Int {

if count(pData) <= 0 || count(pTarget) <= 0 || count(pData) < count(pTarget) { return -1 } // == not found ==
else { }
var wRange  = NSRange(location: 0, length: 0)
wRange  = NSString(string: pData).rangeOfString(pTarget)
if wRange.length > 0 { return wRange.location } // == start index ==
else { return -2 }    // == not found ==
}

wStrigSourceという文字列にwTargetが入っているかどうか調べる方法です。参考まで。

例:var wRange0:Range = wStrigSource.rangeOfString(wTarget)!

var wStrigSource:String = “abc?efe”
var wTarget:String      = “?”
var wRange0:Range   = wStrigSource.rangeOfString(wTarget)!

ただし、実際には、次のように書く必要があります。検索文字列がないときnilが返ってくるようなので、これを無視して検索してあるか・ないかチェックしてしまうと、実行時にエラーになります。

var wStrigSource:String = “abc?efe”
var wTarget:String      = “?”
if wStrigSource.rangeOfString(wTarget)! == nil {
見つからないときの処理
} else {
var wRange0:Range   = wStrigSource.rangeOfString(wTarget)!
見つかったときの処理
}

もうひとつ重要なことは、ヒットした文字位置が返ってくるのですが、Intに直すには、特に注意が必要です。そのほか、startIndex+1と加算する場合も、注意が必要です。

Intに直す方法
var wStartindex:Int = distance(wStrigSource.startIndex, wRange1.startIndex)”)

.startIndex+1
var wIdex = advance(wRange0.startIndex, 1)

Swift:配列で泣き

この一年、わからないことはそれなりに調べているのですが、いい加減になっていることも多いです。

それではまずい事態のようなので、今、やっと真剣に配列について勉強し直しました。サンプルプログラムはあとからアップします。スタディ結果、これから先は、NSMutableArrayを使おうと考えています。

var wDicString:[String]=[] 

  • wDicString[0]=wString 文字列を[wString]のようにカギ括弧でくくらない
  • wDicString[0]=nil  nilは入れられない。空の文字“”ならOK
  • wDicString.append(1234)はダメ。数値(例:Int)は直接入れられない
  • wDicString[2] インデックスで抽出できる。インデックスは実在の範囲内であること。
  • wDicString[1]=wString  既存ならインデックスで置換可能
  • wDicString[999]=wString 実在しないと実行時にエラーになる。
  • wDicString.insert(wString, atIndex: 1) atIndexの前に挿入。実在しないとエラー。
  • wDicString.removeAtIndex(1) atIndexの行を削除。実在しないとエラー。
  • wDicString=[aa, 012345, bb, 1234]のような配列は作れない。
  • (追記) 範囲指定で削除することが可能。削除する開始行位置と終了位置を指定する。次の場合、3番目が削除される。wString[3…4] = []だと3行目から5行目の3行が削除され、removeRangeの終了行と意味が異なるので注意。
    例: wString.removeRange(Range (start: 3,end: 4))

var wDicArray1:[NSArray]=[]

  • wDicArray1.append([wString]) 鉤括弧でくくって[文字列]や[数値]を入れることができる。
  • あとは、var wDicString:[String]=[]と同じ。
  • [ ]の中にどの型を定義するかに処理は依存。
  • 配列の中の配列になるので、 wDicArray1[1][2]のよう書いて取り出す。

2次元配列
var wString:[NSArray] = []

wString.append([0, 1, 2, 3, 4, 5])
wString.append([10, 11, 12, 13, 14, 15])
println(__LINE__,”wString[1]=\(wString[1][1])です”)

var wMutableArray:NSMutableArray=[] 

  • 一次元[aa]、二次元[aa1,aa2]、三次元[[aa11, aa12],[aa21, aa22],[aa31, aa32]]の配列が可能。
  • 文字列、数値を直接入れるこもできる。
  • 鉤括弧でくくって配列(例:[“aa”, “bb”]として入れることもできる。
  • 二次元以上の配列の展開例

2次元配列
var wString3:NSMutableArray = []
wString3[0] = [0, 1, 2, 3, 4, 5]
wString3[1] = [10, 11, 12, 13, 14, 15]
println(__LINE__,”取り出し方は、wString3[0]=\(wString3[0])です。”)
println(__LINE__,”取り出し方は、wString3[1][1]=\(wString3[1][1])です。”)

3次元配列
var wString4:NSMutableArray = []

wString4[0] = [[0,   1,  2],  [3,  4,  5],  [6,  7,  8]]
wString4[1] = [[10, 11, 12], [13, 14, 15], [16, 17, 18]]
wString4[2] = [[20, 21, 22], [23, 24, 25], [26, 27, 28]]
println(__LINE__,”取り出し方は、wString3[0]=\(wString4[0])です。”)
println(__LINE__,”取り出し方は、wString3[1][1]=\(wString4[2][1][0])です。”)
println(__LINE__,”配列の個数の調べ方は、wString4[2][1].count=\(wString4[2][1].count)です。”)

NSMutableArrayに関する注意点

  •  wMutableArray.count 配列個数を調べるときは、.countを使う。
  • 検索・削除: 指定した値をNSRangeで範囲指定して検索して削除する
    wMutableArray.removeObject(wString,inRange:NSRange(location: 1, length: wMutableArray.count – 1)))
  • 削除: NSRangeで指示した削除開始位置から指定行数分を削除する。
    wMutableArray.removeObjectsInRange(NSRange(location:wStart, length: wLines)))
  • 削除: 指定した行を削除する。
    wMutableArray.removeObjectAtIndex(1)

Swift:NSTreeController / NSArrayController相関図

Swift版のツリー制御サンプルプログラムの相関図です。

NSTreeController / NSArrayControllerを使った場合、バインドや接続がどうなっているかわかりにくいので、ドキュメントを起こしました。

Objective-Cとの違いは、プロパティの取り扱いのような気がします。プロパティ自体を機能・用法の両面でよくわかっていないのでうまく説明できないです。自分なりには、Swiftでは単純にグローバル変数として定義し、クラス名やobjectValueで修飾すればいい、と理解しています。ただし、override initとsuper.initはそれなりの配慮( =ちゃんとした理解)が必要な気がします。

1. 相関図

スクリーンショット 2015-05-24 18.48.16

2. 変更点

スクリーンショット 2015-05-24 18.48.39

Swift:ツリー構造のディレクトリ

フォルダやファイルをツリー構造で表示させる方法を考えていました。

一方で、自力で作るためにバッファ(NSArrayの配列)のダイナミック管理をするメソッドを書き上げました。50行くらいの短いメソッドがふたつなのです。短い割にロジックがむずかしく、何回も机上デバッグしたあとでも、難解でした。

自作ツリー表示の良い点は、Table Viewなので細かいところまで制御可能で、これまでいくつか書いたコードとの互換性がいいことです。悪い点は、仮にうまく動いたとしても障害が出たときに手に負えなくなることです。

他方で、1年前に見つけたツリー構造のObjective-CサンプルプログラムをSwiftにコンバージョンすることも考えました。これは、過去3、4回トライして、いずれも力不足で失敗しました。よくわからない箇所があるのと、全ラインをSwiftにコンバージョンできなかったのが原因です。

今回もダメかなと思いました。しかし、コンバージョンは比較的簡単にできました。少しはSwiftが身についたのかもしれません。それで、動かしてみたら、1行だけは表示されました。それで、これだけできれば立派なものと、トレースを追いかけながら慎重にバグを潰しました。

結果、SwiftでNSTreeControlloerを使ったツリー表示ができる見通しが得られました。EXIFアプリやMesaClipに適用する場合、clicked RowとかIBAction、Copy and Dropが想定通り使えるかで機能制限が出てしまいます。この点について、検討する必要があります。

アイコン表示がまだできていません。これは、文字にする予定なので、現時点では深く追求しません。

でもせっかく作ったのだから、この方式で行きたい気もします。

スクリーンショット 2015-05-24 16.10.52

Swift:またまた無駄な動きを発見

前々からおかしいと思っていて、これまたずっと無視してきたのですが、コード量を少なくするために修正しました。

他の調査作業が行き詰まったので、filepathの処理をもっと短く処理できるはずと修正しはじめたのはいいですが、、動く状態に戻すまで2時間はかかってしまいました。やれやれです。

これは、以前、どこかにメモったかもしれません。しかし、ブログからは見つけきれませんでした。それで、改めてメモします。

機能

  • ファイルパスからファイル名と拡張子(ファイルタイプ)を抽出して返します。
  • ファイル名は、ファイルの場合、拡張子を含みます。
  • ファイルパスから実際にフォルダ(ディレクトリ)あるいはファイルが存在するか調べて、結果を返します。

コード

filepathからファイル名・フォルダ名とファイルタイプを抽出する
パラメタ
1. pfilepath:ファイルパス
2. pcheck:指示
①pcheck=”yes”:filepathのチェックを行う
②pcheck=”no”:filepathのチェックを行わない。
返り値
1. rType:ファイルタイプ(拡張子)
2. rName:ファイル名(拡張子有)又はフォルダ名
3. rCheck:
①rCheck=”folder”:ディレクトリ実在
②rCheck=”file”:ファイル実在
③rCheck=”NA”:filepathの実態が存在しない。
④rCheck=””:filepath実在チェックせず
 func CallGetFileInfo(pfilepath:String, pcheck:String) -> (rCheck:String, rName:String, rType:String) {
var wCheck:String = “”  
  var wName:String  = NSFileManager.defaultManager().displayNameAtPath(pfilepath)
  var wType:String  = pfilepath.pathExtension 
  if pcheck != “yes”  { return (wCheck, wName, wType) } 
  else { } 
  wCheck      = CallCheckFilepath(pfilepath)  
  return (wCheck, wName, wType)  
}

指定されたFilepathが存在するかどうかチェックする
func CallCheckFilepath(filepath:String) -> String {
var isDir : ObjCBool = true        
 var wBool:Bool = NSFileManager().fileExistsAtPath(filepath, isDirectory:&isDir)
 if  wBool == true {      
  if isDir.boolValue == true  { return “folder” } 
  else  { return “file” }  
 } else    { return “NA” }