스위프트 언어를 사용하면 self키워드와 Self 키워드를 사용할 때가 종종 있어요.
하지만, 명확하게 self 키워드와 Self 키워드의 용도를 모르고 쓸 때가 있더라구요.
두 키워드의 차이를 명확히 알고 쓰기위해서 한번 비교해봅시다!
우선 키워드들의 개념과 사용 목적에 대해서 정리할게요.
self 키워드는 주로 인스턴스를 가르킨다.
인스턴스 내부에서 인스턴스의 속성을 더 명확하게 가르키기 위해 사용
class Student {
var name = "배지해"
func setName(_ name: String){
self.name = name
}
}
여기서 "함수 setName의 파라미터인 name
"과 "클래스 Student의 저장 속성인 name
"의 변수명이 겹친다. 만약, name = name
이라고 입력하게 된다면, 컴파일러는 두 개의 name
모두를 함수 setName의 파라미터인 name
으로 인식하게 될 것이다. ( 컴파일러는 가장 가까이에 있는 변수로 인식하기 때문에 )
이런 경우에 사용자가 가르키는 name
이 어떤 것인지 컴파일러에게 명확하게 표시하기 위해서 self
키워드를 사용한다.
-> self.name = name
self
는 인스턴스를 가르키기 때문에, self.name
은 "클래스 Student의 저장 속성인 name
" 으로 인식할 것이고, name
은 가까운 것 즉, 함수 setName의 파라미터인 name
로 인식할 것이다.
구조체나 열거형에서 인스턴스 자체의 값을 치환할 때 사용
struct Calculater {
var number = 0
mutating func reset() {
self = Calculater()
}
}
예를 들어, 인스턴스를 초기화시키는 reset() 이라는 함수를 구현한다고 해보자. 인스턴스 자기 자신을 Calculater()로 정의해야한다. 이럴때 self
키워드를 사용할 수 있다.
-> self = Calculater()
mutating 키워드 : 구조체나 열거형에서 자기 자신의 속성을 변경하는 경우에 사용하는 키워드이다.
타입속성 / 메서드에서는 타입 자체를 가르킴.
struct A {
static var a = 0
static func printing() {
print("a는 \(self.a)입니다.")
}
}
타입 속성이나 타입 메서드에서 self
키워드는 타입 자체를 의미한다.
위의 예제를 보면, A
라는 구조체에 타입 저장 속성 a
와 타입 메서드 printing()
이 존재한다. 이때, printing()
이라는 함수에서 타입 저장 속성 a
를 불러오고 싶을 때 self.a
를 사용한다.
어떻게 보면 1의 설명인 인스턴스를 가리키는 설명과 동일하다고 생각할 수 있는데, 아래의 예제를 보자.
struct A {
static var a = 0
func printing() {
print("a는 \(A.a)입니다.")
}
}
위의 코드와 다른점은 self.a
가 아닌 A.a
라고 표현한 부분이다.
printing()
이라는 함수를 구조체가 아닌 인스턴스 메서드로 선언한 경우엔 self.a
라고 표현한다면 에러가 뜬다. a는 구조체의 타입 저장 속성이기 때문에 A의 인스턴스
에서 static 저장 속성 a
에 접근할 수 없기 때문이다. 즉, 타입 자체에서 static 변수에 접근을 해야하기 때문에 A.a
로 써야한다.
외부에서 인스턴스 타입에 접근할 때 사용
class Someclass {
static var something = 0
}
let myclass: Someclass.Type = Someclass.self
타입 인스턴스를 가리키는 경우에 Someclass.self
를 사용한다.
Someclass.Type
은 아직 배우지 않은 메타타입개념이다.
타입 인스턴스 : 클래스/구조체/열거형에서 static을 붙여 선언한 타입 속성이나 타입 메서드들이 데이터 영역에 존재하는 그 자체를 타입 인스턴스라고 한다.
Self 키워드는 주로 타입을 가르킨다.
이 때, 타입은 특정한 타입이 아닌, 특정 타입의 내부에서 해당 타입을 가르킨다.
특정 타입 내부에서 타입을 선언하는 위치에 사용
특정 타입 내부에서 타입속성 / 타입메서드를 지칭하는 위치에서 타입 대신 사용
extension Int {
// 인스턴스 계산 저장 속성
var zero: Self {
return 0
}
// 타입 저장 속성
static var zero: Self = 0
// 인스턴스 메서드
func toZero() -> Self {
return self.zero
}
// 타입 메서드
static func toZero() -> Self {
return Self.zero
}
}
-> var a: Int
변수 a
의 타입을 Int
로 선언하는 코드이다.
위의 예제처럼 Int
라는 타입의 구조체를 확장한다고 했을 때, 변수의 타입 선언에서 Int
타입을 Self
키워드로 사용할 수 있다.
위의 예제의 타입 메서드 toZero()
를 보면 타입속성을 지칭하는 위치에서 Int.zero
를 Self.zero
로 대신 사용할 수 있다.
프로토콜에서 채택하려는 타입을 지칭가능
protocol remote {
func turnOn() -> Self
}
// 스트링 타입을 확장하면서 remote 프로토콜을 채택
extension String: remote {
func turnOn() -> String {
return "tv를 켜다"
}
}
프로토콜에서 함수 turnOn()
의 리턴 타입에 Self
키워드를 넣으면, 해당 remote
프로토콜을 채택하는 함수가 turnOn()
이라는 함수를 선언할 때, 채택하는 함수의 타입이 자동적으로 입력이 된다.
두 키워드의 차이를 아시겠나요?
정리를 해보자면,
구분 | self | Self |
---|---|---|
의미 | 인스턴스 | 타입 |
전체적으로 큰 차이는 인스턴스를 가르키는 것과 타입을 가르키는 것을 알 수 있습니다.