Swift入門メモ -構造体-
22 構造体
構造体とは
クラスとの違い
- メソッドの中で自信のプロパティを書き換えることが出来ない
- 書き換える場合は明示的に指示
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
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
- クラスなどが複雑に入り組んでいた時に、プロパティやメソッドがあるか安全に確かめる方法
- 先頭から調べて行って、
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
- クラスの型をチェックしたり、あるクラスを親クラス・子クラスとして扱うための方法
- クラスの型チェックは
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 クラスを使う
クラスとは
- 関連のある変数や関数をまとめて管理出来る
- クラスの中の変数->
プロパティ
- クラスの中の関数->
メソッド
- クラスを使う時は
インスタンス
というデータの実体を作る
クラスを作る
- クラスのプロパティは初期化されるか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 クラスの継承
継承とは
- あるクラスを元に別のクラスを作る
できること
- 親クラスで定義されたプロパティ、メソッドを全部使うことが出来る
- 親クラスのメソッドを
override
できる override
を禁止することが出来る- 親クラスのメソッドの前に
final
を付ける
- 親クラスのメソッドの前に
- 親クラスのメソッド、プロパティを呼ぶことが出来る
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 プロトコル
プロトコルとは
- 以下のような問題を解決してくれて便利なもの
- すべてのプロパティを受け継がなくていい
- クラスの継承では親クラスを複数持つことができない
- 継承関係がないような複数のクラスに似たような機能を持たせることを保証することができる
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 列挙型
- あるデータが取り得る値を一覧にしたデータ型
- 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 関数を使う
関数とは
- 複数の処理をまとめることができる
基本形
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
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
- ある条件を満たしている間処理を繰り返す
- 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
- 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)") }