Swift Language

Swift Language - 10 - 메소드 (Methods)

xnoag 2023. 2. 1. 09:30

인스턴스 메소드 (Instance Methods)

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


// 다음은 이 클래스를 사용한 (예)입니다.
let counter = Counter()
// 초기 count 값은 0입니다.
counter.increment()
// count 값이 1로 변경 됐습니다.
counter.increment(by: 5)
// count 값은 현재 6입니다.
counter.reset()
// count 값은 0이 됩니다.

 

self 프로퍼티 (The self Property)

// 모든 프로퍼티는 암시적으로 인스턴스 자체를 의미하는 self라는 프로퍼티를 갖습니다. 
// 인스턴스 메소드 안에서 self프로퍼티를 이용해 인스턴스 자체를 참조하는데 사용할 수 있습니다.

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

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


/* 앞의 예제에서는 increment()메소드에서는 count += 1이라 표현했지만 
사실은 self.count += 1의 의미 입니다. 
이것이 가능한 이유는 Swift에서 특정 메소드에서 해당 인스턴스에 등록된 메소드나 프로퍼티를 호출하면 
현재 인스턴스의 메소드나 프로퍼티를 사용하는 것으로 자동으로 가정하기 때문입니다. 
위 규칙이 적용되지 않는 예외적인 상황이 있습니다. 바로 인자 이름이 프로퍼티 이름과 같은 경우 입니다. 
이 경우에는 프로퍼티에 접근하기 위해 명시적으로 self키워드를 사용해야 합니다. 
다음은 self키워드를 이용해 파라미터와 프로퍼티의 모호함을 명확하게 한 (예)입니다. */
struct Point {
    var x = 0.0, y = 0.0
    func isToTheRightOf(x: Double) -> Bool {
        return self.x > x  // self.x를 이용해 프로퍼티 x와 인자 x를 구분, Point의 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")
}
// "This point is to the right of the line where x == 1.0" 출력

 

인스턴스 메소드 내에서 값 타입 변경 (Modifying Value Types from Within Instance Methods)

/* 값 타입 형의 메소드에서 프로퍼티를 변경하고 싶을 때가 있을 텐데요. 어떻게 해야할까요? 
바로 메소드에 mutating붙여 주면 가능합니다. 
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))")
// "The point is now at (3.0, 4.0)" 출력

 

Mutating 메소드 내에서 self 할당 (Assigning to self Within a Mutating Method)

// 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메소드를 사용해 다른 상태를 표현할 수 있습니다.
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

 

타입 메소드 (Type Methods)

// 타입 메소드의 선언은 메소드 키워드 func앞에 static혹은 class키워드를 추가하면 됩니다. 
// static메소드와 class메소드의 차이점은 static메소드는 서브클래스에서 오버라이드 할 수 없는 타입 메소드 이고, class메소드는 서브클래스에서 오버라이드 할 수 있는 타입 메소드 라는 것입니다.

class SomeClass {
    class func someTypeMethod() {
        // 타입 메소드 구현
    }
}
SomeClass.someTypeMethod()    // 타입 메소드 호출!


// 타입 메소드 안에서도 self키워드를 사용할 수 있습니다. 
// 하지만 타입 메소드에서의 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
        }
    }
}
/* advance메소드 앞에 붙은 @discardableResult키워드는 
리턴 값이 있는 메소드에서 리턴 값을 사용하지 않으면 컴파일러 경고가 발생하는데, 
그 경고를 발생하지 않도록 해줍니다. */