[Swift] Methods? Instance Methods & Type Methods

JISU LEE·2021년 12월 26일
1
post-thumbnail

본 글은 Methods - The Swift Programming Language (Swift 5.5)만을 참고하여 작성되었습니다.
거의 번역에 가깝습니다. 혹시 틀린 부분이 있다면 댓글 부탁드립니다🙏🏻

Methods

Method : 특정 유형과 연관된 함수

클래스, 구조체와 열거형은 주어진 유형의 Instance로 작업하기 위해 특정 작업과 기능을 캡슐화 하는 Instance Method들을 정의할 수 있다. 형식 자체와 관련된 method들도 정의할 수 있다. Type method는 Objective-C의 Class method와 유사하다.

구조체와 열거형이 method를 정의할 수 있다는 것은 C, Objective-C와의 주요한 차이점이다. Objective-C에서 클래스는 method를 정의할 수 있는 유일한 유형이다. Swift에서는 클래스, 구조체, 혹은 열거형 중 뭐든지 간에 생성한 유형에 관해 method를 정의할 수 있다는 유연성이 있다.

Instance Methods

Instance Method : 특정 클래스, 구조체, 혹은 열거형의 Instance에 속해 있는 함수

Instance 프로퍼티에 접근하고 수정하는 방법을 제공하거나, Instance의 목적과 관련된 기능을 제공하여 해당 Instance의 기능을 제공한다. Instance method는 함수와 동일한 syntax를 가진다.

Instance method는 해당 유형의 다른 모든 Instance method 및 property에 대한 암시적 접근 권한을 가진다. Instance method는 속한 유형의 특정 Instance에서만 호출할 수 있다. 기존 Instance 없이는 별도로 호출이 불가능하다.

struct|class|enum TypeName {
		func functionName() { //구현 }
}
class Counter {
    var count = 0
    func increment() {
        count += 1
    }
}

self Property

유형의 모든 Instance에는 Instance 자신과 동일한 self 라고 불리는 암시적 프로퍼티가 있다. self 프로퍼티를 사용해서 자체 Instance method 내에서 현재 instance를 참조할 수 있다.

class Counter {
    var count = 0
    func increment() {
        **self**.count += 1
    }
}

실제로 코드에 self를 자주 작성할 필요는 없다. self를 명시하지 않으면 Swift는 method 내의 프로퍼티나 Instance의 property 혹은 method를 참조하고 있다고 가정한다.

이 규칙의 예외는 Instance method의 매개변수 이름이 Instance property와 동일한 이름을 가질 때 발생한다. 이 경우 매개변수 이름이 더 우선시 되기 때문에 프로퍼티를 참조하기 위해서는 더 한정적인 방법을 사용해야 한다. 매개변수명과 property명을 구분하기 위해 self를 프로퍼티를 사용하는 것이다.

struct Point {
    var x = 0.0, y = 0.0
    func isToTheRightOf(x: Double) -> Bool {
        return self.x > x
    }
}

Modifying Value Types from Within Instance Methods

구조체와 열거형은 값 유형이다. 기본적으로 값 유형은 Instance method 내에서 수정할 수 없다.

하지만, 만약 구조체와 열거형의 프로퍼티를 수정할 필요가 있다면, 그 method가 mutating하도록 선택할 수 있다. 선택한 후에 method 내에서 속성을 mutate할 수 있으며, method가 끝날 때 기존 구조체에 다시 기록된다. 이 method는 암시적으로 self 속성에 완전히 새로운 인스턴스를 할당할 수 있고, 이 새로운 인스턴스는 method가 종료되면 기존 instance를 대체한다.

이 method의 func 키워드 앞에 mutating 키워드를 넣어 위 동작을 선택할 수 있다.

struct Point {
    var x = 0.0, y = 0.0
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        x += deltaX
        y += deltaY
    }
}

let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveBy(x: 2.0, y: 3.0)
// this will report an error

상수 구조체 Instance의 속성은 그 속성은 가변 속성이더라도 변경할 수 없기 때문에 mutating method를 호출할 수 없다.

Assigning to self Within a Mutating Method

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

이전 버전과 동일한 표현이다.

열거형에 대한 mutating method는 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
        }
    }
}

Type Methods

Instance method는 특정 유형의 instance에서 호출되는 method이다. Type 자체에서 호출되는 method를 정의할 수 도 있다. 이러한 종류의 method를 type method라고 한다.

Type Method : Type 자체에서 호출되는 method

method의 func 키워드 앞에 static을 작성하여 Type method를 나타낸다. class는 static 대신 class 키워드를 사용하여 하위 클래스가 해당 method의 상위 클래스 구현을 재정의하도록 할 수 있다.

Type method는 Instance method와 같이 . syntax로 호출된다. 하지만, type method는 그 타입의 객체가 아닌 type에서 type method를 호출한다.

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

type method의 본문 내에서 암시적인 self 프로퍼티는 그 타입의 instance가 아니라 type 자체를 참조한다. 즉, instance 프로퍼티 혹은 매개변수와 마찬가지로 self를 사용하여 type 프로퍼티와 type method를 명확히 구분할 수 있다.

일반적으로 정규화되지 않은 type method의 구현 내에서 사용되는 method 및 프로퍼티 명은 다른 type-level method와 프로퍼티를 참조한다. type method는 type 이름을 prefix로 붙일 필요 없이 다른 type method를 호출할 수 있다. 유사하게, 구조체와 열거형에서 type method는 type명을 prefix로 붙일 필요 없이 type 프로퍼티들에 접근할 수 있다.

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
        }
    }
}
profile
iOS / 🌊

0개의 댓글