この本を見ながら進めてる↓
前回⇒「【Swift奮闘記ep12】メソッド(インスタンスメソッド、Selfキーワード、型メソッド)」
メソッドが無事に終わり(50%くらいしか理解できてないけど)、次はサブスクリプトだ。
サブスクリプト
あるクラスが複数のプロパティを持つ場合に、ここのプロパティにアクセスする方法を定義するための機能
これがサブスクリプトの説明であるが、これだけ見ても全くわからんだろう。
バスの座席に人を乗せたり、座席に座っている人を取得したりといったようなことを、サブスクリプトで実現できる。
というのが今回の話。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
class Bus { static let numberOfRows = 4 static let numberOfColumns = 2 static let numberOfSeats = Bus.numberOfRows * Bus.numberOfColumns var seats = [String?](repeating: nil, count: Bus.numberOfSeats) subscript(row: Int, column: Int) -> String? { get { return seats[row * Bus.numberOfColumns + column] } set { seats[row * Bus.numberOfColumns + column] = newValue } } } |
なんのことを言っているかわからない?俺もわからない。
ただ、説明を読んでいくと、とりあえずサブスクリプトの直前までは理解できた。
var seats = [String?](repeating: nil, count: Bus.numberOfSeats)
ってイニシャライザに引数を指定したもので、repeatingが代入する要素、countsには要素の個数を渡す。
「このイニシャライザは、特定の要素を任意の数だけ入った状態の配列をインスタンス化したいときに使います。」とある。なるほど。
nilの要素がnumberOfSeats(8個)分だけ含まれた配列をインスタンス化している?らしいが、もうすでにこれでインスタンス化されていると言っていいのかな?
※追記
配列というクラスをインスタンス化して具体的に配列を作っている感覚だね。
以下と同じ意味らしい。
var seats: [String?] = [nil, nil, nil, nil, nil, nil, nil, nil]
これはズゴイわかりやすかった。
つまりイニシャライザって先に大雑把な形だけ作っちゃいますよって、考えだね!(知らんけど)
[String?]って部分について、String型でオプショナルにラップしている。空席(nil)の場合があるからラップする必要があるんだよね。
オプショナルにラップするの意味がわからない人は「【Swift奮闘記ep8】オプショナルとは?(ラップ、オプショナルチェーン)」にゴー。
で次にサブスクリプト部分を見ていくのだが、これまたよくわからんのである。サブスクリプトの書き方はget構文、set構文を使うという、もうすでに忘れかけているが計算型プロパティと同じ定義の仕方である。
特定の座席の値を変更・取り出し
サブスクリプトの引数として行(row)と列(column)を定義しているので、それを指定することで座席を表現することができる。
シートが8個あってそれらは配列なのでそれぞれ番号がついている。(0〜7)
columnとrowを使って表すと、row * Bus.numberOfColumns + columnと表現できる。
と、ここまででもうすでに頭パンク状態。
その式に実際に数字を代入してみたら、「なるほどそうなるのか〜」と納得はするのだが実際にやってみるとなると???である。
具体的に言うと、4行2列のバスがあったとして、2行1列目はインデックスが2*2+1=5となる。つまり座席の6番目ということ。
※追記
2行1列目はrow=1,column=0で表せるよね。
1*2+0=2
じゃあ座席1番目(つまりインデックス0)はどうやって表現するんだ?
※追記
row(行)もcolumn(列)も0から始まる事に注意すれば理解できた。
つまり座席0番目(一番左上)はrow=0,column=0で表せるよね。
rowもcolumnも0から始まっている点がキーになると思うんだけど・・・。
今の段階だとよくわからない。
その辺の仕組みは置いといて、実際にサブスクリプトをつかってみると何となくプログラミングやっている感じがするので以下のようにやってみてほしい。
let bus = Bus()
// インスタンス化
bus.seats
// シートの状態を見る
// [nil, nil, nil, nil, nil, nil, nil, nil]
bus[0, 1] = “tanaka”
// インデックス1番にtanakaを代入
bus.seats
// 座席状況を確認
// [nil, {some “tanaka”}, nil, nil, nil, nil, nil, nil]
// 2番目(つまりインデックス1番目)にtanakaが入っていることが確認できた。
// someとなるのは今のところあまり気にしないでいい。オプショナル型ということを表している。らしい。
bus[0, 0] = “sato”
// インデックス0番目にsatoを代入。以下全てに代入
bus[1, 0] = “yamada”
bus[1, 1] = “kato”
bus[2, 1] = “suzuki”
bus[2, 2] = “yamakawa”
bus[3, 1] = “kawamura”
bus.seats
// 座席状況を確認
// [{some “sato”}, {some “tanaka”}, {some “yamada”}, {some “kato”}, nil, {some “suzuki”}, {some “yamakawa”}, {some “kawamura”}]
bus[3, 1] = “hikawa”
// kawamuraのかわりにhikawaを代入
bus.seats
// 座席の状況を見ると、ちゃんとhikawaに変わっている。
以上が座席の情報を取得したり変更したりする仕組みなんだね。
なんか、いつも使っている飛行機の座席とるシステムもこういう風に作られているんだ(実際どうなのかは知らないけど)と思ったら楽しくなってきた!
将来これを自分で作れるのかな!ワクワク(多分めっちゃ難しいから俺には無理かも。)
※追記
2周目になると、ほとんど理解できた。
読み取り専用のサブスクリプト
読み取り専用ってことはつまり?どうなるのかな?そこがわからんのだよ。
※追記
読み取り専用ってのは席に誰かを当てはめたり(代入)しないってこと。
もうすでに座っている人たちについて「どこに誰が座っているかを教えてもらう」が読み取りってことだ。
実際にプログラムを見てみればわかるかな。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class ReservedBus { static let numberOfColumns = 2 let seats = ["tanaka", "suzuki", "kato", "sato", "huruta", "yoshida", "saito", "watanabe", "takahashi"] subscript(row: Int, column: Int) -> String { return seats[row * ReservedBus.numberOfColumns + column] } } |
サブスクリプト内でバスの列数が必要であるからstatic let numberOfColumns = 2と定義する。
シートはすでに予約済みで満杯とする。
そのとき、どこに誰が座っているのかを取得できるプログラムだ。
座っている人を変更することはできない。
だから読み取り専用なのだ。(returnのみで処理)
let reservedBus = ReservedBus()
// インスタンス化
reservedBus[2, 1]
// 3行2列目?の座席を確認
reservedBus.seats
// 座席全体の状況を確認
reservedBus[2, 1] = “sato”
// 3行2列目?の座席をsatoに変更しようとしたけどエラーになって変えられない。
ていう感じである。
慣れれば使いこなせるのかなー。
まあちょっとしっくりしない部分はあるけど、先に進むぞ!
続き⇒「【Swift奮闘記ep14】参照カウント(ARCによるインスタンスの生成・破棄、循環参照、強い参照・弱い参照)」
コメント