読者です 読者をやめる 読者になる 読者になる

Swift(Appleプログラミング言語)の使い方リファレンス(サンプルコードあり)

Swift
  • Apple公式SwiftドキュメントLanguage Guideをもとに、
  • 項目は網羅的かつ記述は簡潔な言語リファレンスになるように、
  • プログラミング言語Swiftの使い方をまとめる

という方針で執筆していきます。

下記の構成と項目keywordを考えて書いています。

  1. 変数var,定数let
  2. 整数Int,小数Double、数値型変換
  3. 文字列String,ハッシュ/辞書Dictionary,タプルtuple
  4. 演算子、制御構文if,switch,case,for,for-in,wile,do-while,break,continue,label
  5. 関数func,クロージャ
  6. 列挙enum,クラスclass,構造体struct,型キャストis/as
  7. 拡張extension,プロトコルprotocol,型エイリアスtypealias
  • 黒太文字:執筆完了した項目(まずは、チュートリアルで割愛した項目から)
  • 水色文字:(既にチュートリアルでざっくり説明してあるので)今後執筆予定の項目

プログラミング言語Swift日本語チュートリアルでは、手っ取り早くプログラミングに着手できるように、高度な項目を割愛し、学習項目を抑えた構成にしてあります。

丁寧な解説 or iPhone/iPadアプリ開発情報をお求めの方へ

既にSwiftの書籍が発行されているようですので、参考になさって下さい

先取り!Swift

先取り!Swift

iPhone/iPadアプリ開発の説明は(今のところ)ありません。書籍等を参考になさって下さい

絶対に挫折しない iPhoneアプリ開発「超」入門【iOS7対応】増補改訂版

絶対に挫折しない iPhoneアプリ開発「超」入門【iOS7対応】増補改訂版

Appleプログラミング言語Swiftの使い方まとめ(日本語リファレンス)

タプル

タブルは、任意の型、任意の個数の値をまとめて、ひとつの値として扱えるようにしたもの

let player = (7, Position.Guard, "John") // enum Position { case Guard }みたいなものを仮定
let (number, position, name) = player
let (_, pos, first_name) = player
let player_name = player.2

let player2 = (number: 7, position: Position.Guard, name: "John")
let player_name2 = player2.name
  • 変数に、タプルを分解して代入すれば、普通の変数同様に使える
  • 分解して代入するときに不要な要素は、下線で受け取れば、その値は無視される
  • 要素にはインデックス(ゼロ始まり)でアクセスすることも出来る
  • タプル内で要素名を与えておくと、値を参照するときに使える

表明(アサーション、アサート、Assertion)

アサーションを使って、処理が先に進む前に、必要な条件が成り立っていることをチェックします。
例えば、配列の添字が要素数に納まっていること、外部から受け取った文字列が危険でないことなど。

assert(value_to_be_negative < 0)
assert(value_to_be_negative < 0, "value_to_be_negative actually is not negative")
// assert(value_to_be_negative < 0, "value_to_be_negative: " + String(value_to_be_negative)) // => エラー
// assert(value_to_be_negative < 0, "value_to_be_negative: \(value_to_be_negative)") // => エラー
  • assertを使って、条件とメッセージを定義する
    • メッセージは、
      • 省略可
      • 文字列リテラルのみOK(文字列変数や文字列定数、埋め込みは使えない)
  • 実行時に、その条件が、
    • 成り立たっていなければ、そこで終了し、先に進まなくなる
    • 成り立っていれば、そのまま実行が進む
  • Xcodeから実行した場合は、どのassertで終了したのかわかり、メッセージが出力される

型キャスト

型キャストは、あるインスタンスの型を別の型としてチェックしたり、別の型として扱ったりする仕組み
Swiftでは、ある型がプロトコルに従っているかをチェックしたり、そのプロトコルとして扱ったりするためにも使われる。

class MoviePlayer {  }
class BlueRay : MoviePlayer { func seek() {/* */} }
class Beta : MoviePlayer { func rewind() {/* */} }

func start(mp : MoviePlayer) {
  if mp is Beta {
     let beta = mp as Beta
     beta.rewind()
  }

  (mp as Beta).rewind() // 乱暴
  (mp as? Beta)?.rewind()

  if let beta as? Beta {
     beta.rewind()
  }
}
  • インスタンスがあるクラス(またはそのサブクラス)のインスタンスであることを確かめるためには、is演算子を使う
    • そのクラスのインスタンスであればtrue、でなければfalseになる
    • あるクラスとして扱っているオブジェクトをそのサブクラスのインスタンスとして扱う(downcastする)ためには、
      • ダウンキャストが成功する確証がある場合、asを使う
      • ダウンキャストが失敗するかもしれない場合、as?を使う
  • キャストは、どの型としてそのオブジェクトを扱うかを変更するだけで、オブジェクト自体を変更しない

拡張(Extension)

既存のクラスに対して、定義されているソースコードを使わずに、機能追加する仕組み

extension OriginalTypeA { /* OriginalTypeAに追加したい内容を書く */ }
extension OriginalTypeB: ProtocolX, ProtocolY { /* ProtocolXとProtocolYに従った内容を書く */ }

extension String {
  var hiraganaYomi : String {
    var yomi = ""
    // 略 かっこいいアルゴリズム考えて下さい m(_ _)m
    return yomi
  }
}

extension Int {
  func upto(max: Int, proc: (n: Int) -> ()) { proc(n) }

  mutating func negate() { self = self < 0 ? self : -self }
  
  subscript(n: UInt) -> Int { // n桁めの数字を取り出す
    var x = 1
    for _ in 1..n { x *= 10 }
    return (self / x) % 10
  }

  enum Unit {
    case K = 1024
    case M = 1048576
  }
  func inUnit(unit : Unit) -> Double { return self / unit.toRaw }
}
3.upto(6) { println($0) }
1234[1]
  • 拡張を定義するには、extensionを使う
  • 計算されるプロパティーを追加することが出来る
  • 値を保持するプロパティーを追加することは出来ない
  • 既存のプロパティーのobserverを追加することは出来ない
  • クラスメソッドインスタンスメソッドを追加できる
  • selfやプロパティーを変更するメソッドにはmutatingを付けなければいけない
  • 入れ子にした型を追加できる
  • 添字演算子を追加することが出来る
  • (designated initializer*1以外の)initializerを追加することが出来る
    • designated initializerはオリジナルのクラスで定義されていなければいけない
    • 全てのプロパティーにデフォルト値がありかつinitを定義していない構造体や列挙を拡張する場合は、拡張のイニシャライザーの中から、デフォルトイニシャライザーを呼んだのち、各メンバーのイニシャライザーを呼ぶことが出来る

プロトコル

プロトコルとは、ひとまとまりの機能を提供するために必要なプロパティーやメソッドのひな形

protocol ProtocolX {
  var readOnly: Int { get }
  var readWrite: Int { get set }
  class var typeProperty: Int { get set }
}
class ClassXYZ: ClassZ, ProtocolX, ProtocolY { /* ここに定義 */ }
  • プロトコルを定義するには、protocolを使う
  • 型の定義でプロトコルに従っていることを示すには、型名の後ろに、コロンに続けて、カンマ区切りでプロトコルを記述する
  • コロンの後ろに親クラスも指定したい場合、親クラスを最初に書く
  • プロトコルは型の一種なので、
    • 変数、定数、プロパティー、関数のパラメーター/戻り値、コンテナ要素の型指定に使える
    • プロトコル名は大文字で始めるようにする(小文字も一応可能)
  • varでプロパティーの指定ができる:
    • 名前、型、readオンリーかread/write可能かどちらかの3項目を指定しなければいけない
    • readオンリーであることは{ get }で指定する
    • read/write可能であることは{ get set }で指定する
    • 計算される/されないとか値を保持する/しないは指定できない
    • 型プロパティーである場合は、最初にclassを付ける
protocol ProtocolY {
  func a() -> Int
  class func b() -> Int
}
class Y : ProtocolY {
  func a() -> Int { /* 何らかの実装 */ }
  class func b() -> Int { /* 何らかの実装 */ }
}

protocol ProtocolX {
  mutating func c() -> Int
}
struct X {
  mutating func c() -> Int { /* 何らかの実装 */ }
}
  • インスタンスメソッドと型メソッドを指定できる
  • メソッド定義から本体(つまり{})を除いた形式で指定する
  • デフォルト引数の指定は出来ない
  • メソッドを定義するには、先頭にclassを付ける
  • 構造体や列挙を定義するときにstaticで実装されるにしても、classを付ける(?)
  • メソッドインスタンスを変更することを許可するには、funcの前にmutatingを付ける
    • 構造体や列挙の定義でmutatingメソッドを実装する場合、funcの前にmutatingを付ける必要がある
      • クラス定義の中では不要(mutatingを付けるとエラーになる)
@objc protocol Messenger {
  func sendMessage()
  @optional func compressMessage() -> String
}
class QuickMessenger : Messenger {
  func sendMessage() { /* 何らかの実装 */ }
  func compressMessage() -> String { /* 何らかの実装 */ }
}
class RapidMessenger : Messenger {
  func sendMessage()  { /* 何らかの実装 */ }
}

let qm = QuickMessenger()
let message0 : String = qm.compressMessage()
var m = qm as Messenger
let message1 : String? = m.compressMessage?()

let rm = RapidMessenger()
m = rm as Messenger
let message2 : String? = m.compressMessage?() // => nill
  • プロトコルの条件の中で、実装側に必ずしも必要ではないものには、@optionalを付けておく
    • optional requirementという
    • @optinalを使うプロトコルの定義には@objcを付けなければいけない
      • ※ 付けると、そのプロトコルに従わせることが出来るのはクラスだけになる
  • オプショナルなプロパティー/メソッドの値はオプショナル値になる
    • メソッド定義が普通の(オプショナルでない)型を返しても、呼び出した結果はオプショナル値
class Employee {}
@objc protocol HighSpec { func workHard() }
class SoldierEmployee : Employee, HighSpec { 
  func workHard() { /* 程々に定義してあげて下さい m(_ _)m */ }
 }

func runBusiness(employees: Employee[]) {
  for employee in employees {
    if employee is HighSpec {
      let excellentEmployee = employee as HighSpec
      excellentEmployee.workHard()
    }
    if let workaholic = employee as? HighSpec {
      workaholic.workHard()
    }
    (employee as HighSpec).workHard() // これはあかん。使い方がひどいわ。社員は悪くないんや。
  }
}

@objc protocol Executive {
  func leadMembers()
  func readNumbers()
}
@objc protocol Creative {
  func createProducts()
} 
class TopEngineer : Employee { // クラス定義にはプロトコル指定なし
  func createProducts() { /* 略 */ }
  func operateServices() { /* 略 */ }
  func leadMembers() { /* 略 */ }
  func readNumbers() { /* 略 */ }
}

func isCreativeOrExecutive(employee : Employee) -> Bool {
  return employee is Creative || employee is Executive 
}

let topEngineer = TopEngineer()
isCreativeOrExecutive(topEngineer) // => false

// こいつらできるやないかと気づいたら、
extension TopEngineer : Executive, Creative {} // 後からでもこうしてあげればえぇんやで〜
isCreativeOrExecutive(topEngineer) // true

func assignToImportantProject(superEmployee: protocol<Executive, Creative>) {
  superEmployee.leadMembers()
  superEmployee.createProducts()
}
assignToImportantProject(topEngineer)

エイリアス

型に別名を付けることが出来る

typealias UserId = UInt
var userId : UserId = 3
UserId.min // =>  UInt = 0
String(userId) // => String = "3"
  • typealiasを使う
  • 元の型に対して出来ることが、全てそのまま出来る

*1:そのクラスで定義された全てのプロパティーの値を指定できるinit