趣味で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
}