* 완벽한 번역이 아닌 내용을 한 줄 한 줄 읽는 것에 의의를 두었습니다.
* 파파고의 힘을 빌려 번역했으며 잘못된 번역이 있다면 댓글로 알려주세요.
새로 배운 점
- mutating 메서드는 self 프로퍼티에 완전히 새로운 인스턴스를 할당할 수 있다.
- 열거형에서 mutating 메서드는 암시적 self를 같은 열거형의 다른 case로 설정할 수 있다.
- 메서드의 결과를 사용하지 않을 때 @discardableResult를 붙이면 "Result of call to ~~~ is unused" 경고를 표시하지 않는다.
서론
메서드는 클래스, 구조체, 열거형 안에 정의되는 함수(function)입니다.
메서드는 인스턴스 메서드와 타입 메서드를 정의할 수 있습니다.
인스턴스 메서드는 특정 작업 및 기능을 캡슐화하여 동작하고,
타입 메서드는 타입 그 자체와 관련 있으며 Objective-C의 클래스 메서드와 유사합니다.
스위프트에서는 구조체와 열거형에서 메서드를 정의할 수 있는데 이 점은 C나 Objective-C와의 큰 차이점입니다.
Objective-C에서는 클래스에서만 메서드를 정의할 수 있는데,
Swift에서는 클래스, 구조체, 열거형 중 원하는 타입을 선택하여 메서드를 정의할 수 있습니다.
Instance Methods
인스턴스 메서드는 특정 클래스, 구조체, 열거형의 인스턴스에 속하는 함수입니다.
인스턴스 프로퍼티의 접근 및 수정을 제공하거나
인스턴스 존재 목적(purpose)과 관련된 기능을 제공하여 해당 인스턴스의 기능을 지원합니다.
인스턴스 메서드는 함수(function)에서 말한대로 함수와 완전히 동일한 구문을 사용합니다.
인스턴스 메서드가 속한 타입의 { 와 } 안에 인스턴스 메서드를 작성합니다.
인스턴스 메서드는 다른 모든 인스턴스 메서드 및 해당 타입의 특성에 암묵적으로(implicit) 접근합니다.
인스턴스 메서드는 자신이 속한 타입의 특정 인스턴스에서만 호출할 수 있고,
인스턴스가 없다면 호출할 수 없습니다.
Counter 클래스로 간단한 예시를 봅시다.
class Counter {
var count = 0
func increment() {
count += 1
}
func increment(by amount: Int) {
count += amount
}
func reset() {
count = 0
}
}
Counter 클래스에는 세 가지 인스턴스 메서드가 정의 되어 있습니다.
- increment() : count를 1 증가
- increment(by: Int) : count를 특정 수만큼 증가
- reset() : count는 0으로 초기화
Counter 클래스는 count 변수를 현재 counter 값으로 이용합니다.
인스턴스 메서드는 프로퍼티와 동일하게 .(dot)을 이용해 호출할 수 있습니다.
let counter = Counter()
// the initial counter value is 0
counter.increment()
// the counter's value is now 1
counter.increment(by: 5)
// the counter's value is now 6
counter.reset()
// the counter's value is now 0
The self Property
타입의 모든 인스턴스에서는 self라는 암묵적(implicit) 프로퍼티를 가집니다.
self는 인스턴스 자신과 정확하게 동일합니다.
self 프로퍼티를 사용해 인스턴스 메서드 내에서 현재 인스턴스를 참조할 수 있습니다.
increment()는 이렇게 바꿀 수 있습니다.
func increment() {
self.count += 1
}
실제 코드에서는 "self"를 자주 쓸 필요는 없습니다.
사용자가 명시적으로 self를 작성하지 않더라도
Swift는 메서드 내에서 알려진 프로퍼티 또는 메서드 이름을 사용할 때마다,
현재 인스턴스의 프로퍼티나 메서드를 참조한다고 가정합니다.
이 가정은 Counter 클래스의 세 가지 메서드 내에서 count를 사용하는 것을 보면 증명됩니다.
이 규칙의 주된 예외는 인스턴스 프로퍼티 이름과 인스턴스 메서드의 매개변수 이름이 같을 때입니다.
이 경우 self가 없으면 매개변수를, self를 사용하면 인스턴스 프로퍼티를 의미합니다.
다음 예제는 self를 이용해 매개변수와 인스턴스 프로퍼티를 구분합니다.
struct Point {
var x = 0.0, y = 0.0
func isToTheRightOf(x: Double) -> Bool {
return self.x > x
}
}
let somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOf(x: 1.0) {
print("This point is to the right of the line where x == 1.0")
}
// Prints "This point is to the right of the line where x == 1.0"
Modifying Value Types from Within Instance Methods
구조체와 열거형은 값 타입입니다.
기본적으로 값 타입의 프로퍼티는 인스턴스 메서드 안에서 변경할 수 없습니다.
만약 구조체나 열거형에 속하는 메서드에서 프로퍼티를 변경하고 싶다면 메서드에 mutating 키워드를 붙이면 됩니다.
그러면 메서드는 프로퍼티를 변경할 수 있으며, 모든 변경은 메서드가 끝날 때 기존 구조체가 쓰여진 후에 적용됩니다.
메서드는 암시적인 self 속성에 새로운 인스턴스를 완전히 할당할 수 있으며,
새로운 인스턴스는 메서드가 끝난 뒤에 교체됩니다.
아래는 mutating 키워드를 사용한 예시입니다.
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
print("The point is now at (\(somePoint.x), \(somePoint.y))")
// Prints "The point is now at (3.0, 4.0)"
moveBy(x:y:) 메서드는 mutating 키워드를 붙여 인스턴스 프로퍼티를 직접 수정할 수 있습니다.
mutating 메서드는 상수로 선언한 구조체 타입에서 호출할 수 없습니다.
왜냐하면 let 구조체는 mutating 메서드라도 프로퍼티를 변경할 수 없기 때문입니다.
let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveBy(x: 2.0, y: 3.0)
// this will report an error
Assigning to self Within a Mutating Method
mutating 메서드는 암시적(implicit) self 프로퍼티에 완전히 새로운 인스턴스를 할당할 수 있습니다.
Point 예제는 다음과 같이 작성할 수 있습니다.
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY)
}
}
이 예제의 moveBy(x:y:)는 x, y를 새로 설정한 새로운 구조체를 만드는데,
이 메서드를 호출하면 이전 예제와 동일한 최종 결과를 얻습니다.
열거형에서 mutating 메서드는 암시적 self를 같은 열거형의 다른 case로 설정할 수 있습니다.
enum TriStateSwitch {
case off, low, high
mutating func next() {
switch self {
case .off:
self = .low
case .low:
self = .high
case .high:
self = .off
}
}
}
var ovenLight = TriStateSwitch.low
ovenLight.next()
// ovenLight is now equal to .high
ovenLight.next()
// ovenLight is now equal to .off
Type Methods
지금까지 알아본 인스턴스 메서드들은 모두 인스턴스를 통해 호출했습니다.
타입 메서드는 타입 자체에서 호출되는 메서드입니다.
메서드 func 키워드 앞에 static 키워드를 작성하여 타입 메서드를 나타냅니다.
static 대신 class를 붙이면 하위 클래스가 슈퍼 클래스의 해당 메서드를 오버라이딩 할 수 있도록 허용할 수 있습니다.
Objective-C에서는 클래스에서만 타입 메서드를 정의할 수 있었습니다.
Swift에서는 클래스, 구조체, 열거형에서 타입 메서드를 정의할 수 있습니다.
각 타입 메서드는 명시적으로 지원하는 타입으로 범위가 지정됩니다.
타입 메서드는 인스턴스 메서드처럼 .(dot) 구문으로 호출됩니다.
그러나 인스턴스로 호출하는 것이 아니라 타입으로 호출합니다.
아래는 SomeClass 타입으로 타입 메서드를 호출하는 예시입니다.
class SomeClass {
class func someTypeMethod() {
// type method implementation goes here
}
}
SomeClass.someTypeMethod()
타입 메서드의 self는 인스턴스가 아니라 타입 자체를 참조합니다.
즉 인스턴스의 self와 마찬가지로 타입 프로퍼티 및 타입 메서드 매개변수를 구분할 수 있습니다.
일반적으로, 사용 제한이 없는 메서드와 프로퍼티 이름은 타입 메서드 내에서 다른 타입 메서드와 프로퍼티를 참조하는데 사용됩니다.
타입 메서드는 타입 이름 앞에 접두어를 붙일 필요가 없습니다.
비슷하게 구조체와 열거형의 타입 메서드는 타입 이름 접두사 없이 프로퍼티 이름을 사용하여 타입 프로퍼티에 접근할 수 있습니다.
아래 LevelTracker 구조체 예시를 통해 알아봅시다.
LevelTracker 구조체는 게임의 단게를 통해 플레이어의 진행 상황을 추적할 수 있습니다.
싱글 플레이 게임이지만 여러 플레이어의 정보를 하나의 디바이스에 저장할 수 있습니다.
처음 실행하면 모든 게임 레벨은 잠겨져 있습니다.
플레이어가 레벨을 마칠 때마다 기기 상의 모든 플레이어에게 해제해 줍니다.
LevelTracker 구조체는 타입 프로퍼티와 타입 메서드를 사용해 게임 레벨을 풀어주도록 추적하고 유지합니다.
struct LevelTracker {
static var highestUnlockedLevel = 1
var currentLevel = 1
static func unlock(_ level: Int) {
if level > highestUnlockedLevel { highestUnlockedLevel = level }
}
static func isUnlocked(_ level: Int) -> Bool {
return level <= highestUnlockedLevel
}
@discardableResult
mutating func advance(to level: Int) -> Bool {
if LevelTracker.isUnlocked(level) {
currentLevel = level
return true
} else {
return false
}
}
}
메서드의 결과를 사용하지 않을 때 @discardableResult를 붙이면
"Result of call to advance is unused" 경고를 표시하지 않습니다.
다음 Player 클래스는 LevelTracker 구조체를 이용해 각각의 플레이어 진행을 추적하고 갱신하는 예제입니다.
class Player {
var tracker = LevelTracker()
let playerName: String
func complete(level: Int) {
LevelTracker.unlock(level + 1)
tracker.advance(to: level + 1)
}
init(name: String) {
playerName = name
}
}
Player 클래스는 해당 플레이어의 진행 상황을 추적하기 위해 LevelTracker 인스턴스를 생성합니다.
또한, 플레이어가 특정 레벨을 완료할 때마다 complete(level:)을 제공합니다.
아래는 새로운 Player 인스턴스를 생성하고 플레이어가 1레벨을 완료하는 코드입니다.
var player = Player(name: "Argyrios")
player.complete(level: 1)
print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
// Prints "highest unlocked level is now 2"
1레벨을 완료하면 2레벨이 열립니다.
새로 만든 두 번째 플레이어는 6레벨로 갈 수 없습니다.
아직 잠금 해제가 되지 않았기 때문입니다.
참고