【Swift】基本文法まとめ(中編)

プログラミング初心者の勉強ブログ #133

Swift基礎中編はClassやextension、protocolなど。今まで使ってきたことないような概念や仕組みが色々備わっていて新鮮。引数定義も面白い。?と!など、オプショナル型に慣れる必要がある。計算プロパティはJSでやったgetterとsetterと大体同じ。振り返るとJavaScript→Ruby→Python→PHP→Swiftの順でなんやかんやいろんな言語に触れてきていていて、改めて結構勉強してきたなと実感が湧く。

※前編は【Swift】基礎文法まとめ(前編)にて。

 

目次

[toc]

 

Class

基本

// クラスは「型」として扱う
// 頭は大文字

class User {
    let name: String // propertyと呼ぶ
    var score: Int // property
    init() {
        self.name = "me!"
        self.score = 23
    }
}

// 基本
let user: User = User()

// 省略形
let user = User() // instance

// アクセス・値の変更
print(user.name)
print(user.score)
user.score = 26
print(user.score)

 

イニシャライザ

// 初期化メソッドのこと

class User {
    let name: String
    var score: Int
		
    // イニシャライザ使用時
    init(_ name: String, _ score: Int) {
        self.name = name
        self.score = score
    }
		
    // 引数がなかった時(default設定)
    init() {
        self.name = "bob"
        self.score = 30
    }
}

//let tom = User(name: "tom", score: 23)
let tom = User("tom", 23)
print(tom.name)
print(tom.score)

let bob = User()
print(bob.name)
print(bob.score)

 

計算プロパティ(getとset)

// プロパティを動的に計算させることも可能

class User {
    let name: String
    var score: Int
		
    // 計算プロパティ
    var level: Int {
        get {
            return Int(self.score / 10)
	    // 自明の時はselfの省略可能
        }
        set {
	    // 渡された値をnewValueで受け取ることができる
            if newValue >= 0 {
                score = newValue * 10
            }
        }
    }
		
      // getのみの場合
//    var level: Int {
//        return Int(score / 10)
//    }

    init(_ name: String, _ score: Int) {
        self.name = name
        self.score = score
    }
}

let tom = User("tom", 23)

// getが動く
print(tom.level) // 23/10 = 2

// setに対して値を渡す
tom.level = 5
// すると、scoreが書き換わる
print(tom.score) // 50
// マイナスの値は、newValue >= 0の条件より反応しない
tom.level = -3
// したがって、変化せず50のまま
print(tom.score) // 50

 

プロパティの値の監視(willSetとdidSet)

// willSetとdidSetでプロパティの値の変化を監視できる

class User {
    let name: String
    var score: Int {
		
	// プロパティの値が変わる前の処理
        willSet {
            print("\(score) -> \(newValue)")
	    // newValueで変化後の値を取得可能
        }
				
	// プロパティの値が変わった後の処理
        didSet {
            print("Changed: \(score - oldValue)")
	    // newValueで変化後の値を取得可能
        }
    }
		
    // プロパティが最初に初期化される時は
    // willSetとdidSetは実行されない
    init(_ name: String, _ score: Int) {
        self.name = name
        self.score = score
    }
}

let tom = User("tom", 23)
tom.score = 53
tom.score = 40

 

メソッド作成

class User {
    let name: String
    var score: Int
    init(_ name: String, _ score: Int) {
        self.name = name
        self.score = score
    }
    // method
    func sayHi(_ msg: String) {
        print("\(msg) \(name)")
    }
}

let tom = User("tom", 23)
tom.sayHi("hola")

 

継承

class User {
    let name: String
    var score: Int
    init(_ name: String, _ score: Int) {
        self.name = name
        self.score = score
    }
		
    func sayHi() {
        print("hi \(name)")
    }
    // 頭にfinalをつけるとoverrideされない
}

class AdminUser: User {
    func sayHello() {
        print("hello \(name)")
    }
		
    // 親クラスメソッドの上書き
    override func sayHi() {
        print("[admin] hi \(name)")
    }
}

let tom = User("tom", 23)
let bob = AdminUser("bob", 33)
print(bob.name)
print(bob.score)
bob.sayHi()
bob.sayHello()

 

型プロパティ、型メソッド

class User {
    let name: String
    var score: Int
		
    // 頭にstaticで型プロパティ
    static var count = 0
		
    init(_ name: String, _ score: Int) {
        self.name = name
        self.score = score
				
        // インスタンス化されるたびに、+1
        User.count += 1
    }
    func sayHi() {
        print("hi \(name)")
    }
		
    // override可能な型メソッドにする場合はstaticではなくclass func
    class func getInfo() {
        print("\(count) instances")
    }
}

class AdminUser: User {
    func sayHello() {
        print("hello \(name)")
    }
    override func sayHi() {
        print("[admin] hi \(name)")
    }
    override class func getInfo() {
        print("[admin] \(count) instances")
    }
}

User.getInfo() // 0

let tom = User("tom", 23)
User.getInfo() // 1

AdminUser.getInfo() // 1

let bob = AdminUser("bob", 33)
AdminUser.getInfo() // 2

 

型キャスト(as)

// as  : キャストが成功すると保証されるときに使用(アップキャストなど)
// as! : 強制ダウンキャストの際に使用
// as? : ダウンキャストが成功するか分からない場合に使用(失敗すると戻り値はnil)

class User {
    let name: String
    init(_ name: String) {
        self.name = name
    }
}
class AdminUser: User {}

let tom = User("tom")
let bob = AdminUser("bob")

// User型の配列
// AdminUser型はUser型を継承していることをswiftが判別してくれていて
// エラー出ない
let users: [User] = [tom, bob]

for user in users {
		
    // as?(型キャスト)
    // あるクラスを、他のクラスに変換すること
    // if let文と組み合わせて、
    // AdminUserのみ名前を表示させている
		
    // こっちは一旦User型のuserをAdminUser型に変えてみて、
    // うまくいく場合はuにuserが代入される
    if let u = user as? AdminUser {
        print(u.name)
    }
	
    // こっちはまずuserがAdminUser型か確認している
    // User型のuserをAdminUser型に変えて、
    // 且つoptional型をunwrapする
    if user is AdminUser {
        let u = user as! AdminUser
        print(u.name)
    }
}

 

 

protocol

// クラスに対して「このプロパティやメソッドは絶対実装してね」という約束をさせるために利用する
// Protocolは継承と違って複数適合させることができる
// Protocol自体がProtocolを継承できる

protocol Printable {
    // 読み込み
    var type: String { get }
	
    // 読み書き可能
    var count: Int { get set }
	
    func printout()
}

class User: Printable {
    let type = "Laser"
    var count = 0
    let name: String
	
    init(_ name: String) {
        self.name = name
    }
	
    func printout() {
        count += 1
        print("\(type): \(count)")
    }
}

let tom = User("tom")
tom.printout()
tom.printout()
tom.printout()

 

 

extension

// 既存の型に新しいプロパティやメソッドを追加できる仕組み

// String型の拡張
extension String {
    var length: Int {
        return self.characters.count
    }
}

let msg = "hello"

// lengthが使用可能
print(msg.length)


// protocolとextensionを組み合わせる
protocol Printable {
    func printout()
}

extension Printable {
    func printout() {
        print("now printing...")
    }
}

// User型にprotocolを適用
class User: Printable {
    let name: String
    init(_ name: String) {
        self.name = name
    }
}

let tom = User("tom")
tom.printout() // now printing...

 

 

前編は【Swift】基本文法まとめ(前編)から。

後編は【Swift】基礎文法まとめ(後編)から。

 

まとめ

今日はSwift基礎をブログ3本立てで投稿しようと思う。最近ブログ更新が滞ってしまっている。ブログをアウトプットの手段として書くために割く時間の優先順位が下がりがち。スクールのテキストみてコード書いてモノ作ってブログで最終アウトプットという、DICに通っていた自分の初期の頃の勉強ライフサイクルは、あくまで基盤に充実した教材があることが前提であり、今思うとDICのオンラインテキストはそれを可能にしていた超便利ツールであった。一方で今通っているG’sのテキストは、「これを見ながら順番にコード書けば、理解が曖昧でもまあとりあえずモノ作れちゃう」ようなものは包括的な見本コード集は基本的には存在せず、しっかり授業についていき自走力で基本的に技術を培っていくスタイルなため毛色が違うと感じている。

G’sの授業の進捗のスピード感と生徒の理解度の進捗に乖離は少なからず生じていると思う。授業のキャッチアップに自学は必須であることは当たり前だが、問題は選択授業になってから、その自学をするための授業後の自学サポートがDICと比較して薄い。比較すること自体あまりよろしくないのかもしれない。なぜならG’sは授業に力入れてる(DICの授業は隔週1回しかないため、授業の濃密度で言ったら明らかにG’sの方が良い)。その分、テキストが薄くなるのは確かに納得ではある。だが、授業後の自学をするにあたっては、配布されたpdfだけでは情報が足りない。pdfと授業の両方があって理解が進む。しかし、選択授業のフェーズになってから、肝心の授業はオフラインの1回ポッキリなわけであり、授業を録画してもらうことはできるものの、それはG’s側で必ず手配してくれるわけではなく、「動画取ってください」と依頼をすることが必要条件になっている。オンラインで動画を見れる教材は用意されているが、選択授業などは対象外のためだ。要は授業に置いてかれたとき録画が無いと、キャッチアップが結構しんどいのである。そしてその授業のスピード感はめちゃめちゃ早い。講師側も時間内にカリキュラムを終わらせる必要があるため、生徒の進捗度合いの確認はそこそこに、どんどん先に進む。僕だけがこう感じているわけではなく、生徒みな授業のスピード感は共感してもらえると思う。

とりあえず僕はSwiftの全5回の授業を最大限に有効活用したく思っている。講師のレベルが高いので、授業はとても勉強になる。しかし授業中はただ写経するだけで精一杯で、理解が追っつかない(なんなら写経も追っつかないときある)ので、そんなときは頼りになるドットインストールで復習しようと考えた経緯である。

以上ありがとうございました。

返信を残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA