LoginSignup
2
5

More than 3 years have passed since last update.

忘備録-Swiftの構造体

Last updated at Posted at 2019-12-04

趣味でIOSアプリ開発をかじっていた自分が、改めてSwiftを勉強し始めた際に、曖昧に理解していたところや知らなかったところをメモしています。

参考文献

この記事は以下の書籍の情報を参考にして執筆しました。

構造体の初期化

カスタムイニシャライザ : 構造体ごとに定義できる独自のイニシャライザ
カスタムイニシャライザが定義されてないとき、自動的にシンプルなイニシャライザが使える。

既定イニシャライザ : 構造体の各プロパティに初期値が指定されている場合、型名(構造体の名前)()と書くことでインスタンスを生成できる。
struct Book {
    var name = "hoge book"
    var page = 125
}
var fuga = Book()
print(fuga.name, fuga.page)    // hoge book 125
全項目イニシャライザ : 各プロパディ名を引数として書き連ねる。
var dogBook = Book(name: "dog book")
print(dogBook.name, dogBook.page)    // dog book 125
var oneBook = Book(page: 1)
print(oneBook.name, oneBook.page)    // hoge book 1

全項目イニシャライザを使用する場合はプロパティに初期値を設定する必要がない。
その代わりに初期値が設定されていないプロパティは必ず引数として値を渡す必要がある。

struct Book {
    var name : String
    var page = 125
}
var fuga = Book(name: "hoge book")    // hoge book 125
print(fuga.name, fuga.page)
var dogBook = Book(name: "dog book")    // dog book 125
print(dogBook.name, dogBook.page)
var oneBook = Book(page: 1)    // error

定数に構造体を代入した場合

定数に代入された構造体のインスタンスは、各プロパディの値を変更できない。
この制約は構造体が値型であるためであり、クラスのインスタンスとは違う。(クラスのインスタンスは後ほど勉強します。)

struct Book {
    var name : String
    var page = 125
}
var hoge = Book(name: "hoge book")
hoge.name = "fuga book"
print(hoge.name, hoge.page)    // fuga book 125
let fuga = Book(name: "hoge book")
fuga.name = "fuga book"    // error

構造体に定数プロパティを宣言する場合

最初の一回しか値をセットできない。

struct Book {
    let name : String = "book"
    var page = 125
}
var hoge = Book(name: "hoge book")    // error
struct Book {
    let name : String
    var page = 125
}
var hoge = Book(name: "hoge book")
hoge.name = "fuga book"    //error

カスタムイニシャライザ

initを使って宣言

struct Book {
    var name : String
    var page : Int
    init() {
        name = " hoge book"    //全てのプロパディに対して値を設定しないとエラー
        page = 125
    }
}
var hoge = book()
print(hoge.name, hoge.page)

複数個のイニシャライザ

引数やラベル名を変えて複数定義できる。

struct Book {
    var name : String
    var page : Int
    init() {
        name = "hoge book"
        page = 125
    }
    init(n: String, p: Int) {
        name = n
        page = p
    }
}
var hoge = book()
print(hoge.name, hoge.page)     //hoge book 125
var dogBook = book(n: "dog book", p: 1)
print(dogBook.name, dogBook.page)    //dog book 1

ネスト型(付属型)

ネスト型 : ある構造体、(クラス、列挙型)を構成、使用するために、その構造体と密接に関連する型(構造体)を定義する場合、構造体の一部として宣言できる。

struct SimpleBook {
    var name : String
    var page : Int
    init() {
        name = "hoge book"
        page = 125
    }
    init(n: String, p: Int) {
        name = n
        page = p
    }
}
struct SellBook {
    typealias Book = SimpleBook    //上の構造体
    var book : Book
    var price : Int
    init(name: String, page: Int, price: Int) {
        book = Book(n: name, p: page)
        self.price = price    //プロパティ名と引数ラベルの名前が被っているのでself必須
    }
}
var hoge = SellBook(name: "hoge book", page: 125, price:50000)
print(hoge.book.name, hoge.book.page, hoge.price)    //hoge book 125 50000

構造体のメソッド

構造体にもメソッドが定義できる。

struct Book {
    var name : String
    var page : Int
    init() {
        name = "hoge book"
        page = 125
    }
    init(n: String, p: Int) {
        name = n
        page = p
    }
    func toMessage() -> String{
        "\(name)\(String(page)) ページです。"
    }
}
var hoge = Book()
print(hoge.toMessage())    //hoge book は 125 ページです。

構造体の内容を変更するメソッド

Swiftでは同じ構造体のインスタンスを出来るだけメモリで共有しておく方法を採用しているため、構造体の要素が変更されると共有方法も変える必要がある。
構造体の要素を変更するメソッドを定義する際には「mutating」というキーワードをfuncの前に置く。
コンパイラが要素を変更する処理を事前に知ることで、定数に格納されたインスタンに対しての呼び出し禁止、効率のいい実行コードの作成ができる。

struct Book {
    var name : String
    var page : Int
    var price : Int
    init() {
        name = "hoge book"
        page = 125
        price = 10000
    }
    init(name: String, page: Int, price: Int) {
        self.name = name
        self.page = page
        self.price = price
    }
    mutating func sell(b: Bool) {
        if b {
            price = Int(Double(price) * 0.8)
        } else {
            price = Int(Double(price) * 1.1)
        }
    }
}
var hoge = Book()
print(hoge.price)
hoge.sell(b: true)
print(hoge.price)

let fuga = Book()
fuga.sell(b: true)    //error

タイプメソッド

これまで定義してきたメソッドはインスタンスに対してのみ使用できる。「インスタンスメソッド」に相当するものである。

struct Hoge{
  func add(_ a:Int, _ b:Int) -> Int{
    a + b
  }
}
let hoge = Hoge()
print(hoge.add(3,2))    // 5
print(Hoge.add(3,2))    // error

これに対して、クラスメソッドに相当するメソッドを「タイプメソッド」という。
宣言するときはfunc の前にstaticをつける

struct Hoge{
  static func add(_ a:Int, _ b:Int) -> Int{
    a + b
  }
}
let hoge = Hoge()
print(hoge.add(3,2))    // error
print(Hoge.add(3,2))    // 5 

イニシャライザの中でメソッドを使いたい

イニシャライザの中ではプロパティの初期設定が全て完了していないと、インスタンスメソッドは使えない。

struct Book {
  var name : String
  var page : Int
  var message : String
  init() {
    name = "hoge book"
    page = 125
    message = self.toMessage(n: name, p: page)    // error
  }

  func toMessage(n name:String, p page:Int) -> String{
    "\(name)\(String(page)) ページです。"
  }
}

一つの方法としてタイプメソッドで定義すれば実装できる。

struct Book {
  var name : String
  var page : Int
  var message : String
  init() {
    name = "hoge book"
    page = 125
    message = Self.toMessage(n: name, p: page)    //Book.toMessageとも書ける
  }

  static func toMessage(n name:String, p page:Int) -> String{
    "\(name)\(String(page)) ページです。"
  }
}
var hoge = Book()
print(hoge.message)    //hoge book は 125 ページです。

構造体のプロパティ

Swiftのプロパティの種類
格納型プロパティ : 定数や変数が機能を提供する
計算型プロパティ : 値の参照と更新機能手続で構成
タイププロパティ(静的プロパティ) : 型(構造体名)に関連する情報を示すプロパティ(どんなインスタンスにも共通の値をセットするといい。メモリの節約になる。)

タイププロパティ

struct Book {
    static var name : String = "hoge book"
    static var page = 125
}
let hoge = Book()
print(hoge.name, hoge.page)    // error
print(Book.name, Book.page)    //hoge book 125

格納型プロパティの初期値を式で設定

インスタンスを生成するたびにnumberプロパティがインクリメントされる。

var serialNomber: Int = 0
struct Book {
  var name : String
  var page : Int
  let nomber : Int = Book.setNo()
  init() {
    name = "hoge book"
    page = 125
  }

  static func setNo() -> Int{
    serialNomber += 1
    return serialNomber
  }
}

var book1 = Book()
print(book1.nomber)    // 1
var book2 = Book()
print(book2.nomber)    // 2
var book3 = Book()
print(book3.nomber)    // 3

計算型プロパティ

他のプロパティの値を参照、計算して返す、代入することができる。

struct Book {
  var name: String = "hoge"
  var price: Int
  var tax : Int {
    get{
      Int(Double(price) * 1.1)
    }
    set{
      self.price = newValue
    }
  }
}

var hoge = Book(price: 2500)
print(hoge.name, hoge.price, hoge.tax)    // hoge 2500 2750
hoge.tax = 3400
print(hoge.name, hoge.price, hoge.tax)    // hoge 3400 3740

setを省略する場合getの記述を省略してかける。
getだけを記述することは出来ない。

struct Book {
  var name: String = "hoge"
  var price: Int
  var tax : Int {
      Int(Double(price) * 1.1)
  }
}
var hoge = Book(price: 2500)
print(hoge.name, hoge.price, hoge.tax)    // hoge 2500 2750
hoge.price = 3400    //hoge.taxは使えなくなるので注意
print(hoge.name, hoge.price, hoge.tax)    // hoge 3400 3740

特殊な設定

getにmutatingを記述 : プロパティの値を変えることができる。

struct Book {
  var name: String = "hoge"
  var price: Int
  var tax : Int {
  mutating get{    //getでプロパティ値を変えるときに記述
    price = Int(Double(price) * 1.1)
  }
  set{    
    self.price = newValue
  }
}

setにnonmutating : 背tで値を変更しないとき、構造体を定数に入れててもsetを呼べるようにする。

struct Book {
  var name: String = "hoge"
  var price: Int
  var tax : Int {
  get{
    Int(Double(price) * 1.1)
  }
  nonmutating set{
     print(newValue)
  }
}
let hoge = Book(price: 2500)
hoge.tax = 3400    //構造体が定数に格納されててもsetを呼べる

プロパティオブザーバ

プロパティオブザーバ : 格納型プロパティの値が変更されるのをきっかけに
起動できる処理

struct Book {
  var name: String = "hoge"
  var price: Int
  var tax : Int {
    willSet{
      print("new : \(newValue)")    // new : 108
    }
    didSet{
      print("old : \(oldValue)")    // old : 110
    }
  }
}

var hoge = Book(price: 100, tax: 110)
hoge.tax = 108

添字付け

添字付け:複数のプロパテがあるときに配列のように添字を使ってアクセスできるようにする方法

struct BookShelf {
    var book = ["hoge", "fuga", "inu"]
    var comic = ["JUMP", "oni"]
    var count = 5
    subscript(i: Int) -> String {
            return i < 3 ? book[i] : comic[i-3]
    }
}
let list = BookShelf()
for i in 0 ..< list.count {
print(list[i],terminator: " ")    // hoge fuga inu JUMP oni
}
2
5
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
2
5