티스토리 뷰

반응형

이번 글에서는 Swift 공식 문서의 11번째 단원인 Methods를 읽고 정리한 글을 쓰려고 한다.

 

Apple Swift 공식 문서 11단원 - Methods

Methods

메서드는 특정 타입과 연관된 함수이다. 클래스, 구조체, 열거형은 모두 인스턴스 메서드를 정의할 수 있고 이러한 메서드는 주어진 타입의 인스턴스를 사용하기 위한 기능을 캡슐화한다. 클래스, 구조체, 열거형에서는 타입 자체의 메서드 타입을 정의할 수도 있다.
Swift의 정의할 수 있는 메서드는 C언어와 Objective-C와는 큰 차이점이 있다. Objective-C에서는 클래스에서만 메서드를 정의할 수 있었는데, Swift에서는 클래스, 구조체, 열거형에서 모두 메서드를 정의할 수 있다.


 

Instance Methods

인스턴스 메서드는 특정 클래스, 구조체, 열거형의 인스턴스에 속하는 함수이다. 이러한 메서드는 인스턴스의 프로퍼티에 접근하고 수정하는 방법과 어떠한 목적과 관련된 기능을 제공하여 인스턴스의 기능을 지원한다. 정의하는 방법은 일반 함수와 똑같은 방법으로 정의할 수 있다. 인스턴스 메서드는 해당 타입의 모든 인스턴스 메서드 및 프로퍼티에 내재적인 액세스 권한을 갖는다. 또한 자신이 속한 타입의 인스턴스에서만 호출 가능하다. 이 말은 인스턴스가 없다면 별도로 호출할 수 없다는 말이다.

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

위의 코드에서 Counter클래스는 increment(), increment(by: Int), reset()의 세가지 인스턴스 메서드가 정의되어있다. 이 메서드들은 모두 Count 프로퍼티를 수정하는 기능을 가지고 있다.

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

Counter 클래스의 인스턴스를 만들고 정의된 메서드들을 사용하는 방법은 위의 코드에서 볼 수 있다. 여기서 이름이 같은 메서드가 두 개 있는데 이는 매개변수에 의해 구분된다.

The self Property

모든 인스턴스는 self라는 암시적 속성이 있고 이는 인스턴스 자기 자신을 뜻한다. self 프로퍼티를 사용하여 자기 자신의 메서드나 프로퍼티에 접근할 수 있다.

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

만약 아까 위에서 선언한 Counter 클래스의 increment() 메서드를 self 프로퍼티와 함께 정의하면 위의 코드와 같다. 물론 위의 코드처럼 self를 붙일 필요는 없다. 이는 만약 명시적으로 self를 사용하지 않으면 Swift는 메서드 내에서 알아서 해당 프로퍼티나 메서드를 사용하기 위해 자기 자신의 프로퍼티나 메서드를 참조한다. 하지만 만약 인스턴스 메서드의 매개변수 이름이 인스턴스의 프로퍼티와 이름이 같은 경우에는 반드시 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"

위의 코드처럼 인스턴스의 프로퍼티의 이름도 x이고 인스턴스 메서드의 매개변수 이름도 x인 경우에 self를 붙여줘서 둘을 구분해줘야 한다. 만약 구분하지 않으면 해당 메서드의 매개변수로 모두 처리해버린다.

Modifying Value Types from Within Instance Methods

구조체나 열거형은 값 타입이다. 기본적으로 값 타입의 프로퍼티는 인스턴스 메서드 내에서 수정할 수 없다. 하지만 만약 구조체나 열거형의 인스턴스 메서드에서 프로퍼티를 수정해야 한다면 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)"

위의 코드에서 Point 구조체의 moveBy(x:y:) 메서드는 해당 인스턴스의 x, y 프로퍼티 값을 새로운 값으로 수정하는 메서드이다. 즉 이렇게 프로퍼티를 수정하려면 앞에 mutating 키워드를 써줘서 프로퍼티를 수정 가능하게 만들어줘야 한다. 이렇게 수정을 해줄 수 있기 때문에 이런 기능을 사용하기 위해선 구조체 인스턴스를 var로 선언해줘야 한다. let으로 선언하여 상수로 만들게 되면 오류를 발생시킨다.

Assigning to self Within a Mutating Method

이러한 프로퍼티의 변경 방법은 새로운 인스턴스를 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)
    }
}

위의 코드는 이전에 정의한 Count구조체에서 self를 사용하여 프로퍼티 값을 수정하는 방법이다. 즉 자기 자신에 아예 새로운 인스턴스를 할당해버리는 것으로 아까와 동일한 결과를 발생시킨다.

열거형에서의 Mutating 메서드와 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 is now equal to .high
ovenLight.next()
// ovenLight is now equal to .off

 

Type Methods

위에서 언급한 인스턴스 메서드는 각자의 타입의 인스턴스에서 호출 가능한 메서드였다. 이젠 타입 자체의 메서드를 정의하는 방법에 대해 알아볼 것이고 이것이 타입 메서드이다. 타입 메서드를 정의하는 방법은 func 키워드 앞에 static이라는 키워드를 써주면 된다. 클래스에서는 class 키워드로 이를 대체하며 이는 상속받은 자식 클래스에서 부모 클래스의 메서드를 override 하여 재정의 할 수 있게 하기 위해서다.

class SomeClass {
    class func someTypeMethod() {
        // type method implementation goes here
    }
}
SomeClass.someTypeMethod()

위의 코드는 클래스에서 타입 메서드를 정의하고 사용하는 방법을 나타낸 것이다. 타입 메서드는 . 으로 접근할 수 있다. 위의 코드에서도 볼 수 있듯 인스턴스를 선언하지 않고 바로 타입 그 자체에서 메서드를 호출할 수 있다는 것이다. 이러한 타입 메서드도 self를 사용하여 명확하게 구분할 수 있다. 그럼 이러한 타입 메서드를 사용하는 예를 보며 이해해보자.

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라는 구조체를 정의했고 해당 구조체에는 unlock(_ :), isUnlocked(_ level:) 메서드가 타입 메서드로 정의되어 있고 highestUnlockedLevel은 타입 프로퍼티로 정의되어있다. advance(to:) 메서드는 isUnlocked(_ level:)의 반환 값에 의해 currentLevel을 수정하는 메서드이며 구조체에서 프로퍼티를 수정하기 위해 mutating으로 정의된 것을 볼 수 있다. @discardableResult 속성은 함수가 값을 반환하지만 반환 값을 사용하지 않을 것이라고 컴파일러에게 알려주는 속성인데 자세한 내용은 Attributes에서 볼 수 있다.

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 구조체를 프로퍼티로 만들었다. Player 클래스는 complete(level:) 메서드가 호출될 때마다 LevleTracker의 타입 메서드를 호출한다.

var player = Player(name: "Argyrios")
player.complete(level: 1)
print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
// Prints "highest unlocked level is now 2"

위의 코드처럼 Player 클래스의 인스턴스를 만들고 인스턴스 메서드를 호출하게 되면 LevelTracker의 타입 메서드도 호출되게 되고 타입 메서드가 변화시키는 값이 타입 프로퍼티인 highestUnlockedLevel이었으므로 모든 인스턴스에 영향을 주게 된다.

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함