0
1

Swiftの`self`について調べてみました

Last updated at Posted at 2024-09-04

はじめに

なんとなくself.email = emailみたいに使ってるけど意味を理解して使わないと不意打ちのエラーや、既存のコードを壊さないように修正するのが無理だと思ったのでまとめようと思いました。

大まかなselfの使い方

  1. クラスの中で現在のインスタンスにアクセスする時に利用する。

なんでわざわざselfを使うのか?

例:

class User {
    var name: String

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

上記の例の場合、initの引数にnameがあり、User構造体のなかにもnameというプロパティが定義されています。
この場合

init(name: String) {
    name = name
}

ってかくと、どっちがどっちかわからなくなりますよね?それを防ぐために使います。(こうするとエラーが起きますね)
selfとつけてるほうがUserクラスで定義したプロパティで、ついてないほうが引数です。
ちなみに

init(nickName: String) {
    name = nickName
}

のようにするとselfをつけなくてもいいです。

  1. クロージャ内でインスタンスをキャプチャするためにselfをつける
    *キャプチャとは
    クロージャが定義された時点で、クロージャが外部の変数や定数を保持することです

色々用語があってわかりにくいので実装例をあげますね

class Example {
  var value = 10
  func doSomething() {
    let closure = { [weak self] in
      guard let instance = self else { return }
      print(self.value)
    }
    closure()
  }
}
  • この場合ではselfをキャプチャ
  • selfExsampleクラスのインスタンスを指し、その中のvalueを使えるようにしています。

これをこう思いました
「え?クラスの中にクロージャがかかれてるならselfつけなくてもよくない?」
しかし、これはswiftのルールで「そういうもの」らしいです。
ちなみにクロージャについて知らない人は「名前がない関数」としてこの記事を読んでください。

  1. 型(class, enum, struct)そのものを使いたいとき
//  型そのものを参照する
class Dog {
  var name: String

  required init(name: String) {
    self.name = name
  }

  func bark() {
    print("\(name) is barking!")
  }
}

// インスタンスを生成する
func createDogInstance() {
  let dogType: Dog.Type = Dog.self
  let myDog = dogType.init(name: "Buddy")
  myDog.bark()  // Output: Buddy is barking!
}

あまり書くことがないので、どういう時型そのものを使いたい時と型そのものを操作しない時の違いを書いておきます

  1. 型そのものを使いたい時
    結論: 新しいものを作りたい時
    例:モンハンで新しいキャラクターを作りたくなった時、まず性別を選ぶと思います
    let characterType = Female.self これで女性のキャラクターを選ぶ
    let newCharacter = characterSex.init() これでキャラクターを作成

  2. 型そのものを操作しない
    結論: 既存のものを使う時
    例:
    let myCharacter = Female() もうすでにいるキャラクターを使う

型そのものを操作する: 新しいものを作ったり、選んだりしたい時に使います。
型そのものを操作しない: もう決まったものをそのまま使う時に使います。

用語の意味

参照
オブジェクト(データ)の「場所」を指し示すこと

class Person {
  var name: String

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

ここでいう参照とは例えばiPhoneのアプリのメモリ内でPersonインスタンスが保存されてる場所のこと
実際に参照するのは

let person1 = Person(name: "Taro")

こんな感じ

強参照
あるインスタンス(h)が他のインスタンス(Hoge)を参照する際のデフォルト(何も設定しなかった時)の参照方法です

class Hoge {}
var h: Hoge? = Hoge()

循環参照
強参照が相互に依存し合い、メモリが解放されない状態になること
どういうことか?

class Person {
  var name: String
  var closure: (() -> Void)?

  init(name: String) {
    self.name = name

    self.closure = { [self] in
      print("My name is \(self.name)")
    }
  }
}

var person: Person? = Person(name: "Taro")
self.closure = { [self] in
  print("My name is \(self.name)")
}

このクロージャ内でのself.nameselfPerosonインスタンスを参照すること
ただ、Personインスタンスもクロージャを参照([self]を使っているので)しているので循環参照が起きている

対策としては

class Person {
  var name: String
  var closure: (() -> Void)?

  init(name: String) {
    self.name = name

    self.closure = { [weak self] in
      if let self {
        print("My name is \(self.name)")
      }
    }
  }
}

のように[weak self]をつける

参考文献

0
1
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
0
1