LoginSignup
14
10

More than 5 years have passed since last update.

Swift基礎構文

Last updated at Posted at 2018-06-05

ざっと書きました。すごく見ずらいです


行末尾に;は必要ない(あってもいいが基本はつけない)

・変数定数

変数: var
定数: let

・基本は型推論だがこちらから指定もできる

例)

var a: String = "hello"

・文字列の中で「\」を使うと変数や式などを混ぜることができる

例)

var name = "Taro"
print ("hello \(name)")

・nilの扱い方

Swiftでの「nil」は「何もない状態」のこと
nilになりえるデータ型となりえないデータ型がある
例えばStringやIntはnilになりえない(代入するとエラーになる)
もし代入される恐れがある場合は「Optional型」を指定してあげる必要がある
例)


var a: Optional<String> = nil

↓簡略


var a: String? = nil

Optional型を使うときはその値がnilかどうかチェックする必要がある
例)


if s != nil {
    print (s!)
}

この処理を「unwrap」という
ただこの書き方よりもOptional Bindingという書き方がよくつかわれるらしい
例)


if let value = s {
    print (value )
}

これはsをunwrapしてvalueに代入しているらしい
またもう一つよくあるかきかたで


print (s ?? "this is nil")

というのがあるらしい
これはもしsがnilじゃなければ左辺を出力してnilであれば右辺を出力するという処理になる

・配列

要素配列状に記録する
例)


var users = ["Tanaka", "Ueda", "Sato", "Inoue"]

これも型をこちらで指定できる。

空の配列の作り方


var names = [String]()

値の取り出し方


print (users[0]) //Tanaka

for文ですべての配列の値を取り出すこともできる
例)


for user in users {
    print (user)
}

・タプル

タプルとは、簡単に言うと違う型の値をひとまとめにすることができる(?)
例)


var velue = ("apple", 120)

valueにString型とInt型の二つを代入している


print (value.0) //apple
print (value.1) //120

タプルの要素を変数や定数に代入して取り出す方法もよく使われているらしい
例)


let (product, amount) = value
print (product) //apple
print (amount) //120

このとき片方を使わないという場合にはそのようにする書き方がある
例)


let (product, _) value

この「_」はよく使われるらしい

また初期化するときにあらかじめ名前を付けることもできる
例)


var value = (product: "apple", amount: 120)

タプルはよく使われるらしいので意識して覚える

・集合

Set型
書き方


var numbers: Set = [5, 8, 9]

空の場合


var numbers: Set<Int>()

値があるかの確認「contains」
例)


print (numbers.contains[5]) //true

値の追加と削除「insert」「remove」


numbers.insert(10)
numbers.remove(5)

和集合、積集合、差集合


var a: Set = [5, 8, 9, 10]
var b: Set = [5, 7, 8, 10, 11]


print(a.union(b)) // 5, 7, 8, 9, 10, 11


print(a.intersection(b)) // 5, 8, 10


print(a.subtracting(b)) // 7, 9, 11

・辞書型

Dictionary型


var scores: Dictionary<String, Int> = [
          "Tanaka": 40,
          "Ueda": 34,
          "Sato": 70,
]

短い書き方だと、


var scores = [
          "Tanaka": 40,
          "Ueda": 34,
          "Sato": 70,
]

値の変更や取り出し


scores["Tanaka"] = 50
print (scores["Tanaka"])

こうするとkey”Tanaka”の値を50をに入れ替えたので50が表示されるはずですが、これだけでは不完全
なぜかというとDictionaryでkeyを指定すると値が入っていない場合(nilの可能性)があるのでOptional型で帰ってきてしまう
先の書き方だと


print(scores["Tanaka"]) // Optional(50)

となってしまう
これを防ぐために、nilの場合どうするかを明示的に書いてあげる必要がある
先の「nilの扱い方」にのっとって


print(scores["Tanaka"] ?? 0) // 50

配列と同じようにforで値を取り出せる
例)


for (key, value) in scores {
          print("\(key): \(value)")
}

keyの取り出しには「.keys」を使う


var a = [
    "apple": 100,
    "banana": 200,
]
print(Array(a.keys)) // ["apple", "banana"]

空のDictionary


var a = [String, Int]()

・関数

その宣言を呼び出したときに定義した処理をする
宣言の仕方


func 関数名(){処理}

「func」は関数の宣言
「say()」はその関数の名前。名前の後ろに()をつけてあげる必要がある

呼び出しはその関数の名前を書いてあげればいい
例)


func sayHi(){
    print ("hi")
}
sayHi() // hi

その関数を呼び出したときに値を返してもらうこともできる(戻り値)
例)


func sayHi() -> String {
    return "hi"
}
print (sayHi) // hi

「-> String」で戻す値の型を宣言している。これをしないとエラーになってしまう。

・引数
先の「戻り値」は関数を呼び出すとその呼び出し関数から値を戻してくれるものだったんですが
この「引数」はその逆(?)
呼び出した関数に値を渡すことができる
()の中に書いてあげます
例)


func sayHi(name: String){
    print("hi \(name)")
}
sayHi(name: "tom") // hi tom

これは


func sayHi(変数名: ){}

となっています
呼び出し元では


sayHi(変数名: 渡したい値)

となっています
渡すことができればその変数を関数の中で使うことができるので


print("hi \(name)") // hi tom

となっています

また実行するときの変数と呼び出すときの変数を別々のものにすることもできる
たとえばsayHi(name: "tom")だけではトムがあいさつしているのかトムに挨拶しているのかがあいまいなのでここの変数を変えてみましょう


func sayHi(from name: String){
    print("hi \(name)")
}

sayHi(from: "tom") // hi tom

関数sayHiの()の中が
from name
となっていますが、これは関数の変数nameにfromというラベルを付けてあげています

また呼び出し元に変数をつけたくないとき、


func sayHi(_ name: String){
    print("hi \(name)")
}

sayHi("tom") // hi tom

「_」こちらを使うことで実現できます。

・inout

さきの引数で値を渡すことができることを書いたんですが、実は渡した際に値を受け取る名前は変数ではなく定数になっています


func sayHi(name(←定数: String){}

そのため以下のような処理をするとエラーが起きてしまします


func num(x: Int) {
    x +=5
    print (x)
}

ただやはり受け取った値を変更したいときもありますのでそのときは「inout」というキーワードをつけてあげます


func num(x: inout Int) {
    x +=5
    print (x)
}

ただこのinoutを使うにあたって注意点がある


func num(x: inout Int) {
    x +=5
    print (x)
}

var i = 10

num(x: &i)

このようにしてあげる必要がある

このときnum()でxに5を足しているんですが、書き変わった結果をiにも反映しているので


print (i) // 15

このような結果になる

・クラスの作成

class クラス名 {}
クラスの中に存在する変数や定数を「propaty」という
例)


class User {
        let name: String // propaty
        var score: Int
}

propatyに値を持たせるには「init()」を使用する


class User {
        let name: String
        var score: Int

        init(){
                self.name = "Ueda"
                self.socore = 50
        }
}

ここでの「self」は「Userクラスの」という意味になる

・インスタンスの作成

作ったクラスを使用できるようにする


let user = User()

print(user.name) // Ueda
print(user.score) // 50

・イニシャライザ

先ほどのイニシャライザ[init()]に値を渡すこともできる
インスタンスを作成するときに値を渡してそれをイニシャライザで受け取る
例)


class User {
        let name: String
        var score: Int

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

let user = User(name: "Tanaka",score: 80)

print(user.name) // Tanaka
print(user.score) // 80

この時に宣言する変数名などは先の引数などと同様にラベルを付けて変数名を変えたり、「_」を使用することができる

またイニシャライザは複数用意することができる
例)


class User {
        let name: String
        var score: Int

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

        init(name: String){
                self.name = name
                self.score = 0
        }

        init(){
                self.name = "noneName"
                self.score = 0
        }
}

let user = User(name: "Tanaka",score: 80)
let user2 = User(name: "Tanaka")
let user3 = User()

print(user.name) // Tanaka
print(user.score) // 

print(user2.name) // Tanaka
print(user2.score) // 0

print(user3.name) // noneName
print(user3.score) // 0

これは結果を見てわかる通り、インスタンス化する際の引数の違いで呼ばれるイニシャライザもかわってくる

・計算プロパティ

プロパティは動的に計算させることができる


class User {
        var score: Int
        var level : Int {
                get {
                        return Int(self.score / 10)
                }
                set {
                        self.score = newValue * 10
                }
        }
        init() {
                self.score = 40
        {
}

計算しているプロパティは


var level : Int {
                get {
                        return Int(self.score / 10)
                }
                set {
                        self.score = newValue * 10
                }
        }

この部分になる

これはなにをしているかというと「get」でlevelの値を取得してまた「set」でscoreに値を入れている

なのでこの場合
「level」の値は「score」の10分の1になり「score」は「level」の10倍になる
となる


class User {
        var score: Int
        var level : Int {
                get {
                        return Int(self.score / 10)
                }
                set {
                        self.score = newValue * 10
                }
        }
        init() {
                self.score = 50
        {
}

let user = User()

print (user.score) // 50
print (user.level) // 5

user.score += 10
print (user.score) // 60
print (user.level) // 6

user.score -= 3
print (user.score) // 30
print (user.level) // 3

またsetをしないgetだけの書き方もありその場合は


var level: Int {
        return Int(self.score / 10)
}

このようになる

・プロパティの値の監視

プロパティに変更があったとき動的に処理を行うこともできる
例えばscoreの監視をする際にはこういった書き方をする


var score: Int {
    willSet {
    }
    didSet {
    }
}

willSetについては値の変更がされる前に行われる処理、didSetが書き換わったあとに変更される処理となる
willSetの処理の中で「newValue」というキーワードを使うと変更後の値が取得できる
逆にdidSetの処理の中で「oldValue」というキーワードを使うと変更される前の値を取得することができる
例)


class User {
    var score: Int {
        willSet {
            print ("\(score) -> \(newValue)")
        }
        didSet {
            var i = 0
            i = score - oldValue
            if i > 0 {
                print ("Changed: +\(i)")
            } else {
                print ("Changed: \(i)")
            }
        }
    }

    init() {
        self.score = 50
    }
}

let user = User()

user.score = 40
user.score = 60

// 結果
// 50 -> 40
// Changed: -10
// 40 -> 60
// Changed: +20

またこのwillSetとdidSetに関しては初期化するときには実行されない

・メソッド

簡単に言うとクラスに紐づけられた関数
用語としてメソッドと関数がごっちゃになるので覚えておくようにしたい
使用方法としてはインスタンス化した変数を呼び出す方法と同じで関数名を打ち込んであげればいい
例)


class User {
    var score: Int  

    init() {
        self.score = 50
    }

    func addScore(){
        self.score += 10
        print (self.score)
    }
}

let user = User()

user.addScore() // 60

もちろんこちらにも戻り値や引数も使える

・クラスの継承

ほかの言語と同様継承ももちろんできる
例えばさきのUserクラスを継承してPersonクラスを作ってみる


class User {
    var score: Int  

    init() {
        self.score = 50
    }

    func addScore(){
        self.score += 10
        print (self.score)
    }
}

class Person: User {

}

このPersonクラスはUserクラスを継承しているのでその中身のプロパティやメソッドなども扱うことができる
例)


class User {
    var score: Int  

    init() {
        self.score = 50
    }

    func addScore(){
        self.score += 10
        print (self.score)
    }
}

class Person: User {

}

let person = Person()

person.score = 90
person.addScore() // 100

継承元のクラスのことを親クラス(スーパークラス)、そのクラスをもとに作ったクラスを子クラス(サブクラス)という

・オーバーライド

継承もとのメソッドを使えることはもちろんだが、そのメソッドサブクラスで再定義することができる
これをオーバーライドという
オーバーライドするときは明示的に宣言する必要があり、そのメソッドの頭に「override」として挙げる必要がある
例)


class User {
    var score: Int  

    init() {
        self.score = 50
    }

    func addScore(){
        self.score += 10
        print (self.score)
    }
}

class Person: User {
    override func addScore(){
        self.score += 40
        print (self.score)
    }
}

let person = Person()

person.score = 90
person.addScore() // 130

また継承元のクラスで、そのメソッドをオーバーライドされたくないとき、
そのときは継承元のクラスでそのメソッドに「final」をつけてあげるといい


class User {
    var score: Int  

    init() {
        self.score = 50
    }

    final func addScore(){
        self.score += 10
        print (self.score)
    }
}

class Person: User {
    override func addScore(){
        self.score += 40
        print (self.score)
    }
}

let person = Person()

person.score = 90
person.addScore() // エラー

・staticな要素

クラスのメソッドやプロパティにstaticをつけることでインスタンス化しなくても直接クラスから呼び出せるようにすることができる


class User {
    var score: Int
    static var count = 0

    init() {
        self.score = 50
        User.count += 1
    }

    static func getInfo() {
        print (User.count)
    }

}

User.getInfo() // 0
let user = User()
User.getInfo() // 1

この処理の流れを説明すると
まず以前のクラスたちの違いとしてstaticな変数「count」とstaticなメソッド「getInfo()」がある
まずクラスの下で最初にクラスから直接getInfo()を呼び出している(User.getInfo())
何も処理はしていないので当然帰ってくる値は初期化したままの0
次にUserクラスをインスタンス化している
インスタンス化するときに必ずイニシャライザが呼ばれるが処理を見るとそのイニシャライザでUserのcount に1足している
そのためその下で再度getInfo()を呼び出すと1として値が返される
もちろんこのcountもstaticな要素なのでインスタンス化せずに直接操作することができる
逆にインスタンス化して
user.count = 100
などするとエラーになる

例)


class User {
    var score: Int
    static var count = 0

    init() {
        self.score = 50
        User.count += 1
    }

    static func getInfo() {
        print (User.count)
    }

}


User.count = 100
User.getInfo() // 100

だが


class User {
    var score: Int
    static var count = 0

    init() {
        self.score = 50
        User.count += 1
    }

    static func getInfo() {
        print (User.count)
    }

}


User.count = 100
User.getInfo() // 100

let user = User()
user.coutn = 100 // エラー
User.getInfo()

エラーになってしまう

・型キャスト

あるクラスをほかのクラスに変換すること
書き方


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

class AnotherUser: User {

}

let user = User("sato") //型 -> User
let another = AnotherUser("fujisaki") // 型 -> AnotherUser

let users = [user, another]

2つのインスタンス、userとanotherを配列にしてまとめています。
ここでswiftが自動でanother型「AnotherUser」はuserの型「User」を継承していると判断しているためまとめることができる。継承などを行っていない全く違う型同士だとエラーが発生してしまう
例)エラー


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

class AnotherUser: User {

}

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

let person = Person("takeshi")
let user = User("sato")
let another = AnotherUser("fujisaki")

let users = [user, another, person] // エラ-

これはOK


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

class AnotherUser: User {
}

class Person: User {
}

let person = Person("takeshi")
let user = User("sato")
let another = AnotherUser("fujisaki")

let users = [user, another, person] // OK!!

ただ実際の処理ではクラスごとに分けたいので別途指示してあげる必要がある

まず配列になっているのでfor文でループ処理をする
そのときにAnotherUserの場合にnameを表示させる
「as」というキーワードを使ってuserがAnotherUserか判断する


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

class AnotherUser: User {

}

let user = User("sato")
let another = AnotherUser("fujisaki")

let users = [user, another]

for user in users {
    if let u = user as? AnotherUser {
        print (u.name)
    }
}

・プロトコル

クラスに対して、指定したプロパティやメソッドの実装を強制する
例)


protocol Shutter {
    var count: Int { get set }
    var flash: Bool { get set }
    func flashChange()
    func push()
}


class Camera: Shutter {
    let name: String
    var count: Int
    var flash: Bool

    init(){
        self.name = "Camera"
        self.count = 0
        self.flash = true
    }

    func flashChange() {
        self.flash = !flash
    }

    func push() {
        self.count += 1
        print ("シャッターが押されました")
        if flash == true {
            print ("フラッシュ:ON")
        } else {
            print ("フラッシュ:OFF")
        }
        print ("シャッター回数は \(count) 回目です")
    }

    func test(){
        print("test")
    }
}

let camera = Camera()

camera.flashChange()
camera.push()

作成するときには「protocol」で宣言してあげる
protocol内では実装することを指定してあげるだけなので初期化などはしなくてもいい
そのプロコトルをクラスに紐づけるためにクラス名の横にプロコトル名を宣言する


class Camera: Shutter

・エクステンション

型の拡張

例えばSwiftで文字列の文字数をカウントするには


var str = "abcdefghi"
print(str.characters.count)

として挙げる必要がある
これを


print(str.lengh)

これだけでカウントするようにString型を拡張してみる


extension String {
    var length: Int {
        return self.characters.count
    }
}

var str = "abcdefghi"

print(str.characters.count) // 9
print(str.length) // 9

・値型と参照型

Swiftには主に2つのデータ型がある
IntやDouble、配列などの型は値型
逆にClassだけは参照型になる

違いとしては
引数や変数として渡されるときの挙動が変わってくる
例えば


var a = 20
var b = a
a = 30
print (a) //30
print (b) // 20

これはまずaに20を代入してそのaをbに代入している
そのあとにaに30を代入しているがbにはaの元の値(20)が代入されているだけなので表示をさせるとaが30、bが20となる
ここまでは特に問題はないのだが参照型になるとこの変わってくる


class User {
    var name:String
    init(name: String){
        self.name = name
    }
}

var user = User("ueda")
user.name = "tanaka"

ここまでは普通だが、インスタンス化したuserを別の変数に代入する


ver copy = user

このときどのような処理になっているかというと、
この「copy」はuserが格納されている場所となる
なので例えば


print (user.name) // tanaka

となるが


print (copy.name) // tanaka

同じくこちらもこうなる
こうしてもあくまでcopyはuserの格納されている場所をしめすので結果的にはuser.nameを示すことになる


class User {
    var name:String
    init(name: String){
        self.name = name
    }
}

var user = User("ueda")
ver copy = user

user.name = "tanaka"

print (user.name) // tanaka
print (copy.name) // tanaka

・構造体

struct型
このstruct型は基本的にはclassと同じような使い方をする
例)


struct User {
    var name: String
    init(_ name: String){
        self.name = name
    }
}

var user = User("ueda")

ただクラスと違うところがあり、まず一番の違いは
classは参照型だがstructは値型になる
なので先ほどのインスタンス化したクラスを別の変数に代入するとそのデータの場所が変数に格納されるというものでしたが、このstructの場合は値が格納されることになる
例)


struct User {
    var name: String
    init(_ name: String){
        self.name = name
    }
}

var user = User("tanaka")
var copy = user //userの値
user.name = "ueda"

print (user.name) // ueda
print (copy.name) // tanaka

またそのままではメソッドからプロパティを変更することができない
例えば


struct User {
    var name: String
    init(_ name: String){
        self.name = name
    }

    func changeName() {
        self.name = "sato"
    }
}

こうするとエラーが出てしまう
これを防ぐためにプロパティを変更するときは明示的に「mutating」というキーワードをつける必要がある


struct User {
    var name: String
    init(_ name: String){
        self.name = name
    }

    mutating func changeName() {
        self.name = "sato"
    }
}

そしてclassと違う点としてもう一つ、こちらは継承をすることができない
extensionやprotocolは使うことできるので拡張するばあいはこちらで行う。

・列挙型

enum
値をまとめて型にしたもの


enum 型名 {
    case データ1
    case データ2
    case データ3
    case データ4
}

まとめた値についての処理をしたい場合、その値を取り出して処理を行っていく
例)


enum Ver {
    case iPhone6
    case iPhone7
    case iPhone8
    case iPhone9
    case iPhoneX
}

var ver: Ver
ver = Ver.iPhone6

switch (ver) {
    case .iPhone6:
    print ("This iPhone is \(ver)")
    case .iPhone7:
    print ("This iPhone is \(ver)")
    case .iPhone8:
    print ("This iPhone is \(ver)")
    case .iPhone9:
    print ("This iPhone is \(ver)")
    case .iPhoneX:
    print ("This iPhone is \(ver)")
}

この時、データをすべてcase文に書いていない、またはdefaul処理を行っていない場合エラーになる

また列挙型のデータに対して文字列は数字の値を割り当てることができる
割り当てる型を宣言してあげてそれぞれのデータに代入していく


enum Ver :String{
    case iPhone6 = "ios6"
    case iPhone7 = "ios7"
    case iPhone8 = "ios8"
    case iPhone9 = "ios9"
    case iPhoneX = "ios10"
}

この割り当てた値に対しては「rawValue」を使い取り出すことができる


enum Ver :String{
    case iPhone6 = "ios6"
    case iPhone7 = "ios7"
    case iPhone8 = "ios8"
    case iPhone9 = "ios9"
    case iPhoneX = "ios10"
}

print(Ver.iPhone6.rawValue) // ios6

・ジェネリクス

メソッドなどの作成のさい、使いまわしができるように型を汎用的にさせることができる
例えば何か表示させるメソッドを作りたいと思ったとき

func show(_ x: String){
    print (x)
}

show("hello")

このような処理になる

ただこの処理を例えばInt型を表示させたい。Double型を表示させたいとなった場合、それぞれの処理を書かなければいけなくなりコードが肥大化してしまう。それを解決するためにこのメソッドの型を汎用化させる

メソッド名の後ろに「<T>」を記入してあげて引数の型も「T」にしてあげる
ちなみにこの「T」とくに指定はないようだが、Typeの「T」がよく用いられるそう。

func show<T>(_ x: T){
    print (x)
}

これで型が汎用化されたので引数に文字列、数字と入れても処理をしてくれる


func show<T>(_ x: T){
    print (x)
}

show("hello") // hello
show(555) // 555

・サブスクリプト

インスタンスに対して、配列などのようにインデックスをつけられるようにする


class User{
}

let user = User()
user[0]

サブスクリプトを使うためにはクラス側でそのインデックスに対する配列と処理を記入する
クラスで作成する配列についてはとくに通常通りだが、処理に関しては「subscript」と宣言する


class User {
    var users = ["ueda", "tanaka", "sato"]
    subscript(index:Int) -> String {
        get {
            return users[index]
        }
        set {
            users.insert(newValue, at:index)
        }
    }
}

let user = User()

print (user[0]) // ueda
user[0] = "kikuchi"
print (user[0]) // kikuchi

subscriptの引数indexにはsubscriptで呼ばれる型、今回は整数だったのでInt、引数には文字列を返すので
->Strng
としてあげた
処理のなかの「get」と「set」に関しては計算プロパティとほとんど同じ
値を取得する処理をgetに、設定する処理をsetにする
値を取得するだけならばsetの処理は書かなくてもいい

・guard

異常処理をわかりやすくする

適当に異常処理が発生する処理を書いてみる

func show(_ item: String?) {
    if let value = item {
        print (item)
    } else {
        print ("There is not item")
    }
}

show(nil) // There is not item

これで異常理が発生した際にThis can't runの文字列が表示されるようになった。
ただこれだとコードが肥大化したときにわかりづらくなる

「early return」と「early exit」をつかう

これは異常な処理などが発生した場合、すぐに処理を抜けるというような書き方をする


func show(_ item: String?) {
    if item == nil {
        print ("There is not item")
        return
    } else {
        print (item!)
    }
}

show(nil) // There is not item
show("apple") // apple

最初にitemがnilかどうか判断してあとからunwrapして表示させるという処理になっている
ただこれではまだ可読性は高いとは言えない

そこで「guard」を使用する


func show(_ item: String?) {
    guard let s = item else {
        print ("There is not item")
        return
    }
    print (s)
}

show(nil) // There is not item
show("hi")

もしitemがnilでなければsに代入、そうでなければelseの処理をするという具合になる
ちなみにguardを使用するとunwrapしても変数をそのまま使用することができるので後の「print (s)」に「!」を使用していない

・Optional Chaining

14
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
10