Swift入門メモ -構造体-

22 構造体

dotinstall.com

構造体とは

  • クラスとほぼ似た感じでプロパティやメソッドをまとめたデータ型
  • プロトコル、イニシャライザーなども使える
  • 構造体の宣言はstruct

クラスとの違い

  • メソッドの中で自信のプロパティを書き換えることが出来ない
    • 書き換える場合は明示的に指示
    • funcの前にmutating
  • 継承が出来ない
  • コピーするときの挙動が違う
    • クラス:参照渡し
      • メモリを効率的に使うため
    • 構造体:値渡し
      • わかりやすいが、大きな構造体だとコピーするたびにメモリを圧迫するので注意が必要
struct UserStruct {
    var name: String
    var score: Int = 0
    init(name: String) {
        self.name = name
    }
    mutating func upgrade() {
        score++
    }
}


class User {
    var name: String
    var score: Int = 0
    init(name: String) {
        self.name = name
    }
    func upgrade() {
        score++
    }
}

// ====クラスのインスタンスコピー
var hoge = User(name: "Hoge")
var hoge2 = hoge // hoge2にはhogeの実体を指し示す参照先がコピーされる
hoge2.name = "hoge2" // hoge2のnameプロパティ書き換え
hoge.name // hoge2 に変わる

// ※hogeとhoge2は全く同じデータを指し示している


// ====構造体のインスタンスコピー
var huga = UserStruct(name: "Huga")
var huga2 = huga // huga2にはhugaの値をそのまま丸ごとコピーしている
huga2.name = "huga2" // huga2のnameプロパティ書き換え
huga.name // Huga のまま(hugaとhuga2は別物だから)

クラスと構造体の使い分け

  • 継承が必要、大きなデータを扱う -> クラス
  • シンプルなデータ -> 構造体

Swift入門メモ -get/set,willSet/didSet・Optionl Chaining・Type Casting-

19 get/set,willSet/didSet

dotinstall.com

get/set

  • プロパティを動的に計算
  • プロパティの値を取得する時と設定する時に計算式が書ける
class Student {
    var name: String
    var number: Int
    var attendance: Int = 0
    var rate: Int {
        get {
            // selfは省略出来る
            return Int(Float(attendance) / 10.0 * 100)
        }
        set {
            // 渡された値を「newValue」でとることが出来る
            // attendanceの値を書き換える
            attendance = Int(newValue / 10)
        }
    }
    
    init(name: String, number: Int) {
        self.name = name
        self.number = number
    }
    func attended() {
        attendance++
    }
}

var mayama = Student(name: "rika", number: 3)
mayama.attendance // 0
mayama.rate // 0
mayama.attendance = 1
mayama.rate // 10
mayama.attendance = 3
mayama.rate // 30


// rateを更新してattendanceの値を変える
mayama.rate = 100
mayama.attendance // 10
mayama.rate = 80
mayama.attendance // 8
  • getしか使わない場合
class Student {
    var name: String
    var number: Int
    var attendance: Int = 0
    var rate: Int { // getだけの場合はreturnを書くだけ
        return Int(Float(attendance) / 10.0 * 100)
    }
    
    init(name: String, number: Int) {
        self.name = name
        self.number = number
    }
    func attended() {
        attendance++
    }
}

willSet/didSet

  • プロパティの状態監視
  • 値が変わった時、変わる前と変わった後で何らかの処理を入れることができる
  • プロパティが最初に初期化されるときには実行されない
class Student {
    var name: String
    var number: Int
    var attendance: Int = 0 {
        willSet { // 変わる前にしたい処理
            // 新しい値は「newValue」
            println("willSet: \(attendance) -> \(newValue)")
        }
        didSet { // 変わった後にしたい処理
            // 古い値は「oldValue」
            println("didSet: \(oldValue) -> \(attendance)")
        }
    }
    var rate: Int {
        return Int(Float(attendance) / 10.0 * 100)
    }
    
    init(name: String, number: Int) {
        self.name = name
        self.number = number
    }
    func attended() {
        attendance++
    }
}


var mayama = Student(name: "rika", number: 3)
mayama.attendance = 9 // willSet: 0 -> 9 / didSet: 0 -> 9
mayama.attendance = 10 // willSet: 9 -> 10 / didSet: 9 -> 10

20 Optionl Chaining

dotinstall.com

  • クラスなどが複雑に入り組んでいた時に、プロパティやメソッドがあるか安全に確かめる方法
  • 先頭から調べて行って、nilだった場合はそこで評価を止め、安全に終了させてnilを返す
  • 複雑になると、?マークがたくさんついていく
class User {
    var blog: Blog? // Blog型のOptional
}

class Blog {
    var title = "My Blog"
}

var gawao = User()

// ================
// ★blogプロパティが存在する場合は以下のように取れる
// gawao.blog = Blog()
// Unwrapしてtitleを取得
// gawao.blog!.title // My Blog


// ================
// ★blogプロパティがnilの場合 「gawao.blog!.title」でエラーになる
// Optional Chainingを使う
gawao.blog?.title // nil

// ================
// ★よく書くやり方
// titleがある場合はtを表示する
// - nilの場合はエラーなく終了
// - gawao.blogがある場合は「My Blog」と表示される
if let t = gawao.blog?.title {
    println(t)
}

21 Type Casting

dotinstall.com

  • クラスの型をチェックしたり、あるクラスを親クラス・子クラスとして扱うための方法
  • クラスの型チェックはis
  • asを使ってあるクラスを子クラスに変換することをダウンキャストするという
  • どのようなオブジェクトのインスタンスでもいいという特殊なデータ型がある AnyObject
class User {
    var name: String
    init(name: String) {
        self.name = name
    }
}

class AdminUser: User {}

let kashiwagi = User(name: "hinata")
let matsuno = AdminUser(name: "rina")

// クラスの型が違うがエラーにならない
let users: [User] = [kashiwagi, matsuno]

// クラスごとに処理を分けたいとき
// 型チェック、変換に使える-> is / as

for user in users {
    // AdminUserクラスか調べる->is
    if user is AdminUser {
        // UserクラスからAdminUserクラスにダウンキャスト->as
        let u = user as AdminUser
        println(u.name) //  rina
    }
}
    // userをダウンキャスト->うまくいったらブロックの処理。うまくいかなかったらnilを返す
    if let u = user as? AdminUser {
        println(u.name) //  rina
    }

// ====↓の部分は↑のように書き換えることが出来る====

//    // AdminUserクラスか調べる->is
//    if user is AdminUser {
//        // UserクラスからAdminUserクラスにダウンキャスト->as
//        let u = user as AdminUser
//        println(u.name) //  rina
//    }
class User {
    var name: String
    init(name: String) {
        self.name = name
    }
}

class AdminUser: User {}

class SomeUser {}

let kashiwagi = User(name: "hinata")
let matsuno = AdminUser(name: "rina")
let nakayama = SomeUser()


// 継承関係にないインスタンスが含まれているのでエラーになる
// let users: [User] = [kashiwagi, matsuno, nakayama]

// どのようなオブジェクトのインスタンスでもOKなAnyObjectを使う
let users: [AnyObject] = [kashiwagi, matsuno, nakayama]

Swift入門メモ -クラス・クラスの継承・プロトコル-

16 クラスを使う

dotinstall.com

クラスとは

  • 関連のある変数や関数をまとめて管理出来る
  • クラスの中の変数->プロパティ
  • クラスの中の関数->メソッド
  • クラスを使う時はインスタンスというデータの実体を作る

クラスを作る

  • クラスのプロパティは初期化されるかOptionalになる必要があるので、以下はエラーになる
class Student {
    var name: String // TODO 初期化されていない
    var number: Int // TODO 初期化されていない
    var appear: Int = 0
    func sayHello() {
        println("出席番号 \(number), \(name) say hello")
    }
    func update() {
        appear++
    }
}
  • イニシャライザーを作って初期化
  • インスタンス化してプロパティ等にアクセス
class Student {
    var name: String
    var number: Int
    var appear: Int = 0
    // イニシャライザーを作る
    init (name: String, number: Int) {
        // selfはそのクラスのインスタンス
        self.name = name
        self.number = number
    }
    func sayHello() {
        println("出席番号 \(number), \(name) say hello")
    }
    func update() {
        appear++
    }
}

// インスタンス化
var yasumoto = Student(name: "ayaka", number: 5)
yasumoto.name // ayaka
yasumoto.number // 5
yasumoto.sayHello() // 出席番号 5, ayaka say hello
yasumoto.appear // 0
yasumoto.update()
yasumoto.appear // 1

17 クラスの継承

dotinstall.com

継承とは

  • あるクラスを元に別のクラスを作る

できること

  • 親クラスで定義されたプロパティ、メソッドを全部使うことが出来る
  • 親クラスのメソッドoverrideできる
  • overrideを禁止することが出来る
  • 親クラスのメソッド、プロパティを呼ぶことが出来る
    • superを付ける
class Student {
    var name: String
    var number: Int
    var appear: Int = 0
    // イニシャライザーを作る
    init (name: String, number: Int) {
        // selfはそのクラスのインスタンス
        self.name = name
        self.number = number
    }
    func sayHello() {
        println("出席番号 \(number), \(name) say hello")
    }
    // override禁止 / final
    final func update() {
        appear++
    }
}

// Studentクラスを継承
class ExchangeStudent: Student {
    func reset() {
        appear = 0
    }
    // 親クラスのメソッドを上書き
    override func sayHello() {
        super.sayHello() // 親クラスのメソッドを呼ぶ
        println("出席番号 \(number) は永久欠番です!!")
    }
}


var yasumoto = Student(name: "ayaka", number: 5)
yasumoto.name // ayaka
yasumoto.number // 5
yasumoto.sayHello() // 出席番号 5, ayaka say hello
yasumoto.appear // 0
yasumoto.update()
yasumoto.appear // 1

// yasumoto.reset() // エラーになる。ExchangeStudentしか使えない

var hirono = ExchangeStudent(name: "suzuki", number: 8)
hirono.name // suzuki
hirono.number // 8
hirono.appear // 0
hirono.update()
hirono.appear // 1
hirono.reset()
hirono.appear // 0

// 出席番号 8, suzuki say hello
// 出席番号 8 は永久欠番です!!
hirono.sayHello()

18 プロトコル

dotinstall.com

プロトコルとは

  • 以下のような問題を解決してくれて便利なもの
    • すべてのプロパティを受け継がなくていい
    • クラスの継承では親クラスを複数持つことができない
  • 継承関係がないような複数のクラスに似たような機能を持たせることを保証することができる
protocol Idol {
    // プロパティは読み取り専用か、読み取り&設定両方可能かを指定出来る
    var group: String {get set}
    func sayGroupName() // プロトコルの中では実際の実装を書かない
}

// プロトコルを適用
// プロトコルが複数ある場合はカンマで区切る
// 親クラスを継承してからプロトコルを適用する場合は、継承してからカンマで区切ってプロトコルを適用
class Student: Idol {
    var name: String
    var number: Int
    var appear: Int = 0
    var group: String = "EBC" // プロトコルのプロパティ
    func sayGroupName() { // プロトコルのメソッド
        println("We are \(group)!!!")
    }
    
    // イニシャライザーを作る
    init (name: String, number: Int) {
        // selfはそのクラスのインスタンス
        self.name = name
        self.number = number
    }
    func sayHello() {
        println("出席番号 \(number), \(name) say hello")
    }
    // override禁止 / final
    final func update() {
        appear++
    }
}

var yasumoto = Student(name: "ayaka", number: 5)
yasumoto.group // EBC
yasumoto.sayGroupName() // We are EBC!!!

Swift入門メモ -列挙型-

15 列挙型

dotinstall.com

  • あるデータが取り得る値を一覧にしたデータ型
  • 1文字目は必ず大文字
// 1文字目は必ず大文字
enum Result {
    case Success
    case Error
}

var r: Result
r = Result.Success

// rがResult型というのはわかっているので型を省略して書くこともできる
r = .Success
  • 値を割り当てる
// Int型の値を割り当てる
enum Result: Int {
    case Success = 0
    case Error // 次にcase文が出てきたら値を1ずつ増やしてくれるので1になる
}

// Errorの値を取り出す
Result.Error.rawValue // 1
  • 関数を定義する
enum Result: Int {
    case Success = 0
    case Error
    // String型を返す関数を定義
    func getMessage() -> String {
        // self でデータが入ってくる
        switch self {
        case .Success:
            return "OK!!!"
        case .Error:
            return "NG!!"
        }
    }
}

// 関数を使う
Result.Error.getMessage() // NG!!

Swift入門メモ -関数-

13,14 関数を使う

dotinstall.com

dotinstall.com

関数とは

  • 複数の処理をまとめることができる

基本形

func sayHello() {
    println("Hello")
}

sayHello() // 関数呼び出し「Hello」と表示

引数

基本

func sayHello(name: String) {
    println(name + " say hello")
}

sayHello("プラニメ") // 「プラニメ say hello」と表示

引数を渡すときに明示的に何のデータか表示したい場合

// mynameが渡ってきた時にnameに対応させる
func sayHello(myname name: String) {
    println(name + " say hello") // プラニメ say hello
}

sayHello(myname: "プラニメ") // 「プラニメ say hello」と表示

呼び出すときに渡すキーと関数内で使う変数名が一緒の場合

// 「#」をつけると同じ名前で渡されていると分かる
func sayHello(#name: String) {
    println(name + " say hello") // プラニメ say hello
}

sayHello(name: "プラニメ")

引数の初期値

func sayHello(#name: String = "プラニメ") {
    println(name + " say hello")
}

sayHello() // 引数がない場合は初期値を表示
sayHello(name: "gawao") // gawao say hello

返り値

// 整数型の引数を2つ取り、整数型を1つ返す
func sum(a:Int, b:Int) -> Int {
    return a + b
}

println(sum(5, 11)) // 16


// 複数の返り値->タプルが使える

func swap(a: Int, b: Int) -> (Int, Int) {
    return (b, a)
}

println(swap(5, 11)) // (11, 5)

関数に渡した引数を書き換える

渡した値を書き換える

  • 関数に値を渡すときは値のコピーが渡されていて、関数の中で処理されてもオリジナルの値が変わるわけではない
// 関数の中で値を変えているので、引数に「var」を付ける
func f(var a:Int) {
    a = 20 // 関数の中では「20」
}

var a = 5
f(a)
a // 「5」のまま
  • オリジナルの値を変える場合はinoutを使う
func f(inout a:Int) {
    a = 20
}

var a = 5
// 渡す時に「&」を付ける
f(&a)
a //「20」になる

Swift入門メモ -Optional-

12 Optional

dotinstall.com

  • Optionalではない型はnilになり得ない決まり
  • あるデータがnilかどうか曖昧なままだと重大なエラーが起きやすいという傾向があるため(※よくわかってない)
var s: String
s = nil // エラーになる
  • nilになり得る型をOptionalにしなくてはいけない
var s: String? // Optionalにする
s = nil // エラーにならない
  • Optional型の場合は値を取り出すときに!を付ける
  • ! を付けてOptional型の値を取り出すことをUnwrapするという
let name: String? = "gawao"
// let msg = "hello " + name // エラーになる
let msg = "hello " + name! // hello gawao
  • Unwrapする際は、必ず値がnilではないことを最初に確認する
let name: String? = "gawao"

// if文でチェック
if name != nil {
    let msg = "hello " + name!
}

// よく使うやり方
// 一時的にsにnameの値を入れる
// 値が入ったら条件が真になる
// 中が実行される
if let s = name {
    let msg = "hello " + s // 値が入るのはわかっているので「!」は不要
}
  • 暗黙的にUnwrapされるOptional
  • あるOptional型がnilになり得ないとわかっているときのみ使う
  • 初期化の時に一瞬nilになるが、そのあとは値をずっと保持するオブジェクトを扱う時に使ったり(※よくわかってない)
var label: String!
label = "score" // すでにUnwrapされている
println(label)

Optionalのところ殆ど理解してない。
アプリの開発をしていく過程で理解出来るか・・・

Swift入門メモ -while・do...while・for-

10 while,do…while

dotinstall.com

  • ある条件を満たしている間処理を繰り返す
  • whileは条件判定が前
  • do…whileは条件判定が後なので、必ず1回実行される
// while
var n = 0

while n < 10 {
    println(n)
    n++
}
// do…while
var n = 20
do {
println(n) // 必ず1回は実行されるので 20 と表示される
n++
} while n < 10

11 for

dotinstall.com

  • for文による繰り返し
// 0から9まで表示
for var i = 0; i < 10; i++ {
    println(i)
}
  • ループの途中で処理をスキップするときはcontinue(whileにも使える)
// ループの途中で処理をスキップ
for var i = 0; i < 10; i++ {
    // 3は表示されない
    if i == 3 {
        continue
    }
    println(i)
}
  • ループの途中で処理を抜ける場合はbreak
// ループの途中で処理を抜ける
for var i = 0; i < 10; i++ {
    // 0,1,2を表示して処理終了
    if i == 3 {
        break
    }
    println(i)
}
// 範囲演算子を使って0から9を表示
for i in 0...9 {
    println(i)
}
  • 配列、辞書型データでfor…in…を使う
// 配列
let a = [5, 3, 11]
for i in a {
    // 5, 3, 11が表示されて終わる
    println(i)
}

// 辞書型
let d = ["yasumoto": 5, "kobayashi": 11]
// key, valueをタプルで受け取る
for (k, v) in d {
    println("key: \(k), value: \(v)")
}