J
[Swift] self / Self 본문
1. `self`: 현재 인스턴스를 나타내는 키워드
`self`는 현재 인스턴스 자체를 참조.
Swift에서 모든 인스턴스 메서드와 이니셜라이저에는 암묵적으로 `self`가 포함되어 있으며, 필요한 경우 이를 명시적으로 사용 가능.
1.1 사용 목적
- 현재 인스턴스 참조
- 메서드나 이니셜라이저 내부에서 `self`를 사용하면, 해당 메서드가 호출된 인스턴스를 참조.
- 이를 통해 인스턴스 프로퍼티에 접근하거나 인스턴스 메서드를 호출할 수 있음.
- 이름 충돌 해결
- 이니셜라이저나 메서드의 파라미터 이름이 인스턴스 프로퍼티와 동일한 경우, `self`로 인스턴스 프로퍼티를 참조.
- 클로저 캡처
- 클로저 내부에서 `self`를 사용하면, `self`가 클로저 외부의 인스턴스를 참조.
1.2 동작 원리와 예제
struct Car {
var brand: String
func displayBrand() {
print("The car brand is \\(self.brand).") // self는 생략 가능
}
}
let car = Car(brand: "Tesla")
car.displayBrand() // "The car brand is Tesla."
- 현재 인스턴스 참조
- `self`는 메서드 내부에서 암묵적으로 사용.
struct Rectangle {
var width: Double
var height: Double
init(width: Double, height: Double) {
self.width = width // self.width는 인스턴스 프로퍼티를 참조
self.height = height
}
}
- 이름 충돌 해결
- 이니셜라이저에서 지역 변수와 인스턴스 프로퍼티의 이름이 같을 경우 `self`를 사용해 구분.
class Counter {
var count = 0
func increment() {
DispatchQueue.global().async { [weak self] in
guard let self = self else { return }
self.count += 1
print("Count: \(self.count)")
}
}
}
- 클로저 캡처
- 클로저 내부에서 `self`를 참조할 경우 강한 캡처가 발생하므로, 순환 참조를 방지하려면 `[weak self]` 또는 `[unowned self]`를 사용.
1.3 주의사항
- 지역 변수와 이름이 겹치지 않으면 생략 가능
- `self`는 필요하지 않은 경우 생략할 수 있으나, 명시적으로 사용하는 것이 코드 가독성을 높이는 경우도 존재.
- 클로저에서의 `self`사용
- 클로저가 `self`를 강하게 캡처할 경우, 순환 참조 문제가 발생할 수 있음.
- 클래스와 구조체의 차이
- 클래스의 경우 `self`는 참조 타입이며, 값이 변경되면 모든 참조에 영향을 미치게 됨.
- 구조체에서는 값 타입이므로 메서드에서 `self`를 직접 수정하려면 `mutating` 키워드를 사용해야 함.
2. `Self`: 현재 타입을 나타내는 키워드
`Self`는 현재 타입을 참조.
Swift의 타입 시스템에서 `Self`는 특정 타입을 일반화하여 나타내며, 타입 메서드, 팩토리 메서드, 프로토콜의 연관 타입에서 주로 사용.
2.1 사용 목적
- 현재 타입 참조
- 타입 메서드에서 현재 타입을 참조할 때 사용되며, 이는 클래스, 구조체, 열거형 모두에서 유용.
- 팩토리 메서드
- 현재 타입의 새로운 인스턴스를 생성하여 반환하는 메서드를 작성할 때 사용.
- 프로토콜과의 연관성
- 프로토콜에서 `Self`를 사용하면, 구현체의 타입을 동적으로 참조할 수 있음.
2.2 동작 원리와 예제
struct Shape {
static func createShape() -> Self {
return Self() // 현재 타입 반환
}
}
- 현재 타입 참조
- `Self`는 타입 메서드 내에서 현재 타입을 명확히 참조.
class Animal {
class func createInstance() -> Self {
return self.init() // Self를 사용해 현재 타입의 인스턴스 생성
}
required init() {}
}
class Dog: Animal {}
let dog = Dog.createInstance()
print(type(of: dog)) // Dog
- 팩토리 메서드
- 팩토리 메서드는 상속받은 클래스에서도 적절히 동작하도록 `Self`를 활용할 수 있음.
protocol Copyable {
func copy() -> Self
}
class Document: Copyable {
var content: String
init(content: String) {
self.content = content
}
func copy() -> Self {
return type(of: self).init(content: self.content)
}
}
- 프로토콜에서의 사용
- 프로토콜에서 `Self`는 구현체의 타입을 나타냄.
- 위 코드에서 `Self`는 `Document` 타입으로 대체.
2.3 주의사항
- 타입 제약이 있는 경우
- `Self`는 주로 팩토리 메서드에서 사용되며, 클래스나 구조체의 초기화와 관련.
- 프로토콜 구현 시의 중요성
- 프로토콜에서 `Self`를 사용하면 구현체 타입을 동적으로 결정할 수 있지만, 이는 제네릭 타입보다 덜 직관적일 수 있음.
- 하위 클래스에서의 동작
- 팩토리 메서드에서 `Self`를 반환하면, 하위 클래스에서도 올바르게 동작.
3. `self`와 `Self`비교 요약
키워드 | 의미 | 주요 용도 |
`self` | 현재 인스턴스 참조 | 메서드나 이니셜라이저 내부에서 현재 인스턴스를 참조하거나 전달 |
`Self` | 현재 타입 참조 | 팩토리 메서드, 프로토콜의 연관 타입, 타입 메서드에서 사용 |
4. 결론
`self`와 `Self`는 Swift에서 각각 현재 인스턴스와 타입을 참조하며, 서로 다른 맥락에서 중요한 역할.
`self`는 인스턴스의 속성 및 메서드를 명확히 참조하거나 전달할 때 사용.
`Self`는 동적 타입 참조 및 팩토리 메서드 관련 작업에서 사용.
'Swift' 카테고리의 다른 글
[Swift] init (0) | 2024.04.18 |
---|---|
[Swift] weak, unowned (0) | 2024.04.18 |
[Swift] associatedtype (0) | 2024.04.18 |
[Swift] Protocol (0) | 2024.04.18 |
[Swift] Generic (0) | 2024.04.15 |