Swift) Method

6 minute read

Methods

메서드는 특정 타입과 관련된 함수다. 클래스, 구조체 및 열거형은 모두 지정된 타입의

인스턴스로 작업하기 위한 특정 작업 및 기능을 캡슐화하는 인스턴스 메서드를 정의할 수 있다.

타입 자체와 연관된 타입 메서드를 정의 할 수도 있다.


인스턴스 메서드

인스턴스 메서드는 특정 클래스,구조체 또는 열거형의 인스턴스에 속하는 함수이다.

인스턴스 프로퍼티에 접근하고 수정하는 방법을 제공하거나 인스턴스의 목적과 관련된

함수를 제공하여 해당 인스턴스의 함수를 지원한다.

인스턴스 메서드는 함수에 설명 된대로 함수와 정확히 동일한 구문을 갖는다.

속한 타입의 여는 중괄호와 닫는 중괄호 안에 작성한다. 인스턴스 메서드에는 다른 모든

인스턴스 메서드와 해당 타입의 프로퍼티에 대한 암시적 접근 권한이 있다.

자신이 속한 타입의 특정 인스턴스에서만 호출 할 수 있다. 기존 인스턴스 없이는

격리 된 상태로 호출 할 수 없다.

class Counter {
    var count = 0
    func increment() {
        count += 1
    }
    func increment(by amount: Int) {
        count += amount
    }
    func reset() {
        count = 0
    }
}

위 코드에서 Counter 클래스는 세가지 인스턴스 메서드를 정의한다.

  • increment() - 카운터를 1씩 증가
  • increment(by: Int) - 카운터를 지정된 정수만큼 증가
  • reset() - 카운터를 0으로 재설정

Counter 클래스는 또한 현재 카운터 값을 추적하기 위해 변수 프로퍼티인 count를 선언한다.

프로퍼티와 동일한 도트 구문을 사용하여 인스턴스 메서드를 호출한다.

let counter = Counter()
counter.increment()
counter.Increment(by: 5)
counter.reset()

함수 매개변수는 함수 인수 레이블 및 매개변수 이름에 설명 된대로

이름과 인수 레이블을 모두 가질 수 있다. 메서드는 타입과 관련된 함수일 뿐이므로

메서드 매개변수도 마찬가지이다.


The self Property (자기 자신)

타입의 모든 인스턴스에는 self라는 암시적 프로퍼티가 있으며 이는 인스턴스 자체와

정확히 동일하다. self 프로퍼티를 사용하여 자체 인스턴스 메서드 내에서 현재 인스턴스를 참조한다.

func increment() {
    self.count += 1
}

실제 코드에 self를 자주 작성할 필요는 없다. self를 명시적으로 작성하지 않으면

스위프트는 메서드 내에서 알려진 프로퍼티 또는 메서드 이름을 사용할 때마다 현재

인스턴스의 프로퍼티 또는 메서드를 참조한다고 가정한다.

이 가정은 Counter에 대한 세가지 인스턴스 메서드 내애세 count(self.count 대신)를 사용하여 입증한다.

이 규칙의 주요 예외는 인스턴스 메서드의 매개변수 이름이 해당 인스턴스의 프로퍼티와

동일한 이름을 가질 때 발생한다. 이 상황에서는 매개변수 이름이 우선시되므로

보다 규정 된 방식으로 특성을 참조해야 한다. self 프로퍼티를 사용하여 매개변수 이름과

프로퍼티 이름을 구분한다.

아래 코드에서 self는 x라는 메서드 매개변수와 x라고도하는 인스턴스 프로퍼티를 명확하게 한다.

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("이 점은 x == 1.0인 선의 오른쪽에 있다.")
}

self 접두사가 없으면 스위프트는 x의 두 가지 사용이 x라는 메서드 매개 변수를 참조한다.


값 타입내에서 인스턴스 메서드 수정

구조체와 열거형은 값 타입이다. 기본적으로 값 타입의 프로퍼티는

인스턴스 메서드 내에서 수정할 수 없다.

그러나 특정 메서드 내에서 구조체 또는 열거형의 프로퍼티를 수정해야하는 경우

해당 메서드에 대한 동작을 변경하도록 선택할 수 있다.

그런 다음 메서드는 메서드 내에서 프로퍼티를 변경할 수 있으며 메서드가 끝날 때

변경 한 내용은 원래 구조에 다시 기록된다. 메서드는 암시적 self 프로퍼티에

완전히 새로운 인스턴스를 할당 할 수도 있으며 새 인스턴스는 메서드가 종료 될 때

기존 인스턴스를 대체한다.

해당 메서드의 func 키워드 앞에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("포인트는 지금 (\(somePoint.x), \(somePoint.y))")

Point 구조체는 Point인스턴스를 일정량 이동하는 mutating moveBy(x:y:)메서드를 정의한다.

새 포인트를 반환하는 대신 이 메서드는 실제로 호출 된 포인트를 수정한다.

mutating키워드는 프로퍼티를 수정할 수 있도록 정의에 추가된다.

상수로 저장된 인스턴스의 저장 프로퍼티에 설명 된대로 변수 프로퍼티이더라도 프로퍼티를 변강할 수

없기 때문에 구조체 타입의 상수에 대해 mutating 메서드를 호출 할 수 없다.

let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveBy(x: 2.0, y: 3.0) // Error

mutating 메서드에서의 self 할당

mutating 메서드는 암시적 self프로퍼티에 완전히 새로운 인스턴스를 할당 할 수 있다.

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)
    }
}

위 코드의 mutating moveBy(x:y:)메서드는 x,y 값이 대상 위치로

설정된 새 구조를 만든다. 이 코드는 위에서 구현했던 코드와 동일하게 호출된다.

열겨형을 변경하는 메서드는 암시적 self 매개변수를 동일한 열거형과

다른 케이스로 설정할 수 있다.

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는 이제 .high와 같다.
ovenLight.next() // ovenLight는 이제 .off와 같다.

이 코드는 3개 상태의 스위치에 대한 열거형을 정의하였다.

스위치는 next()메서드가 호출 될 때마다 세가지 전원 상태 사이를 순환한다.


타입 메서드

인스턴스 메서드는 특정 타입의 인스턴스에서 호출하는 메서드이다.

타입 자체에서 호출되는 메소드를 정의할 수도 있다.

이런 종류의 메서드를 타입 메서드라고 한다. 메스드의 func 키워드 앞에 static 키워드를

작성하여 타입 메서드를 나타낸다.

클래스는 대신 class 키워드를 사용하여 하위 클래스가 해당 메서드의

슈퍼 클래스 구현을 재정의 할 수 있다.

스위프트 에서는 모든 클래스, 구조체 및 열거형에 대한

타입 메서드를 정의 할 수 있다. 각 타입 메서드는 지원하는 형식으로 명시적으로 범위가 지정된다.

타입 메서드는 인스턴스 메서드와 동일하게 도트 구문으로 호출된다.

그러나 해당 타입의 인스턴스가 아닌 타입에 대해 타입 메서드를 호출한다.

class SomeClass {
    class func someTypeMethod() {
        // 타입 메서드 구현은 여기서 한다.
    }
}
someClass.someTypeMethod()

타입 메서드의 본문 내에서 암시적 self 프로퍼티는 해당 타입의 인스턴스가 아닌

타입 자체를 참조한다. 즉, 인스턴스 프로퍼티 및 인스턴스 메서드 매개변수에서와 마찬가지로

self를 사용하여 타입 프로퍼티와 타입 메서드 매개변수를 명확하게 할 수 있다.

보다 일반적으로 타입 메서드 본문 내에서 사용하는 정규화되지 않은 메서드 및 프로퍼티 이름은

다른 타입 메서드 및 프로퍼티를 참조한다.

타입 메서드는 타입 이름을 접두사로 사용할 필요없이 다른 메서드의 이름으로

다른 타입 메서드를 호출 할 수 있다. 마찬가지로 구조체, 열거형의 타입 메서드는

타입 이름 접두사 없이 타입 프로퍼티의 이름을 사용하여 타입 프로퍼티에 접근 할 수 있다.


아래 예제 코드는 게임의 다양한 레벨 또는 단계를 통해 플레이어의 진행 상황을 추적하는

LevelTracker라는 구조체를 정의한다.

싱글 플레이어 게임이지만 단일 기기에 여러 플레이어에 대한 정보를 저장할 수 있다.

게임이 처음 플레이 될 때 모든 게임 레벨(레벨 1 제외)이 잠긴다.

플레이어가 레벨을 완료 할 때마다 해당 레벨은 장치의 모든 플레이어에 대해 잠금 해제된다.

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
        }
    }
}

LevelTracker 구조체는 모든 플레이어가 잠금을 해제 한 최고 레벨을 추적한다.

이 값은 가장 높은 잠금 해제 레벨이라는 타입 특성에 저장된다.

LevelTracker는 또한 higherUnlockedLevel 프로퍼티와 함께 작동하는

두 가지 타입 메서드를 정의한다. 첫 번째는 unlock(_:)이라는 타입 메서드로,

새 레벨이 잠금 해제 될 때마다 highesUnlockedLevel 값을 업데이트 한다.

두 번째는 isUnlocked(_:)라는 편의 타입 메서드로, 특정 레벨 번호가 이미 잠금 해제 된 경우

true를 반환한다. (이러한 타입 메서드는 LevelTracker.highestUnlockedLevel로

작성할 필요없이 최상위 UnlockedLevel 타입 프로퍼티에 접근 할 수 있다.)

currentLevel프로퍼티를 관리하기 위해 LevelTracker는 advance(to:)라는

인스턴스 메서드를 정의한다. currentLevel을 업데이트 하기 전에 이 메서드는 요청 된

새 레벨이 이미 잠금 해제되었는지 여부를 확인한다.

advance(to:)메서드는 실제로 currentLevel을 설정 할 수 있는지 여부를

나타내는 부울 값을 반환한다.

advance(to:)메서드를 호출하여 반환 값을 무시하는 코드가 반드시 실수는 아니기 때문에

이 함수는 @discardableResult 속성으로 표시된다.

LevelTracker 구조체는 아래에 코드 Player 클래스와 함께 사용되어

개별 플레이어의 진행 상황을 추적하고 업데이트 한다.

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:)메서드를 제공한다.

이 방법은 모든 플레이어의 다음 레벨을 잠금 해제하고 플레이어의 진행 상황을

업데이트하여 다음 레벨을 이동한다. (앞줄에서 LevelTracker.unlokc(_:)을 호출하여

레벨이 잠금 해제 된 것으로 알려져 있으므로 advance(to:)의 Boolean 반환 값은 무시된다.)

새 플레이어에 대한 Player 클래스의 인스턴스를 만들고 플레이어가 레벨 1을 완료하면

어떻게 되는지는 확인할 수 있다.

var player = Player(name: "Argyrios")
player.complete(level: 1)
print("잠금 해제 된 최고 레벨은 지금 \(LevelTracker.highestUnlockedLevel)")
// "잠금 해제 된 최고 레벨은 지금 2"

게임에서 아직 잠금 해제되지 않은 레벨로 이동하려는 두 번째 플레이어를 만드는 경우

해당 플레이어의 현재 레벨 설정 시도는 실패한다..

player = Player(name: "Beto")
if player.tracker.advance(to: 6) {
    print("플레이어는 현제 6레벨이다.")
} else {
    print("6레벨은 아직 잠금 해제 되지 않았다.")
}
// "6레벨은 아직 잠금 해제 되지 않았다."

Tags:

Categories:

Updated:

Comments