Swift study - Methods

rbw·2022년 3월 16일
0

swift-study

목록 보기
14/17
post-thumbnail

Swift - Methods

메소드는 특정 타입과 연관된 함수이다. 클래스, 구조체 그리고 열거형은 특정 행동과 주어진 타입의 인스턴스와 함께 작업하는 기능성을 캡슐화한 모든 인스턴스 메소드를 지정가능하다. 클래스, 구조체 그리고 열거형은 타입과 스스로 연결된 타입 메소드도 지정이 가능하다. 타입 메소드는 Objective-C의 클래스 메소드와 유사하다.

스위프트에서 구조체와 열거형에서 메소드를 정의하는 사실은 C, Objective-C와는 많이 다르다. Objective-C내의 클래스는 메소드를 정의할 수 있는 유일한 타입이다. 스위프트에서는, 클래스, 구조체 또는 열거형을 지정가능하고 커스텀 타입에 대한 메소드를 지정하기 위해 유연성을 가지고 있다.

Instance Methods (인스턴스 메소드)

인스턴스 메소드는 특정 클래스, 구조체 또는 열거형의 인스턴스에 속하는 함수이다. 인스턴스들의 기능을 지원한다. 기능으로는 인스턴스 프로퍼티의 변경과 접근을 위한 방법이나 인스턴스의 목적에 관련된 기능을 제공한다. 인스턴스 메소드는 함수의 문법과 동일한 구문을 사용한다.

속한 타입의 중괄호 안에 인스턴스 메소드를 작성한다. 인스턴스 메소드는 해당 타입의 다른 모든 인스턴스 프로퍼티와 메소드에 암시적 접근권한을 가진다. 인스턴스 메소드는 해당 타입에 속해있는 특정 인스턴스에서만 불린다. 기존의 인스턴스 없이 독립적으로 호출은 불가하다.

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

카운터 클래스는 세가지 인스턴스 메소드를 정의한다.

  • increment()는 카운터를 1 증가한다.
  • increment(by: Int) 특정 숫자로 카운터에 증가시킨다.
  • reset() 카운터를 0으로 재설정한다.

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

함수 파라미터는 인자라벨과 이름을 모두 가질 수 있다. 메소드 파라미터와 동일하다. 이는 메소드는 타입과 연관된 함수이기 때문이다.

The self Property

타입의 모든 인스턴스는 self로 불리는 명시 프로퍼티를 가진다. 이는 해당 인스턴스 그 자체와 동일하다. self 프로퍼티를 사용해서 자체 인스턴스 메소드 내부에서 현재 인스턴스를 참조한다.

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

실제로, 코드에서 self는 자주 작성할 필요는 없다. 명시적으로 작성하지 않는다면, 스위프트는 메소드내부의 알려진 프로퍼티나 메서드 이름을 사용할 때마다 현재 인스턴스의 프로퍼티 또는 메소드를 참조하고 있다고 가정한다. 이 가정은 카운터 내부에 세개의 인스턴스 메소드인 count를 사용함으로 증명된다.

이 룰에 대표적 예외는 인스턴스 메소드에 파라미터 이름이 해당 인스턴스 프로퍼티와 이름이 같을 때 발생한다. 이런 경우에, 파라미터 이름이 먼저 가져가고, 더 한정된 방식으로 프로퍼티를 참조해야 한다. 파라미터 이름과 프로퍼티 이름을 구별하기 위해 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")
}
// true ! 
// Prints "This point is to the right of the line where x == 1.0"

self 접두사가 없으면 스위프트는 x의 두 사용 모두를 파라미터의 메소드로 참조해서 사용한다.

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

값 타입은 구조체와 열거형이다. 기본적으로 값타입의 프로퍼티는 인스턴스 메소드 내에서 변경이 불가하다.

만약, 특정 메소드내에서 구조체나 열거형의 프로퍼티를 변경할 필요가 있다면, 그 메소드에 대한 동작을 변경할 수 있다. 그 다음 메서드는 메서드 내에서 프로퍼티를 변경 할 수 있으며 변경사항은 메서드가 종료될 때 원래 구조체에 기록된다. 메소드는 또한 암시적으로 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)"

// let으로 정의하면 에러가 발생한다.
let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveBy(x: 2.0, y: 3.0)
// this will report an error

moveBy는 실제 값을 리턴하는것 대신에, 호출될 때 실제 값을 변경한다.

구조체 타입 상수로는 위 메소드를 호출할 수 없다. 이 프로퍼티는 변수 속성이여도 변경될 수 없기 때문이다.

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

변경 메서드는 새 인스턴스 전부에 암시적으로 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)
    }
}

위 메소드는 새로운 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 is now equal to .high
ovenLight.next()
// ovenLight is now equal to .off

세게의 다른 케이스로 상태를 표현하고 있다.

Type Methods (타입 메소드)

위에 설명한 인스턴스 메소드는 특정 타입의 인스턴스에서 호출되는 메소드이다. 타입 스스로 호출되는 메소드도 정의 가능하다. 이런 메소드의 종류는 타입메소드 라고 불린다. static 키워드를 함수 앞에 작성함으로써 나타낸다. 클래스는 class 키워드로 대체할 수 있고, 하위클래스가 해당 메서드의 상위클래스의 구현을 오버라이드 할 수 있게한다.

NOTE

Objective-C에서, 클래스에만 타입-레벨 메소드를 정의가능하다. 스위프트에서는 모든 클래스, 구조체와 열거형에서 정의가 가능하다. 각 타입 메소드는 지원하는 타입으로 명시적으로 범위가 지정된다.

타입 메소드는 인스턴스 메소드와 같이 점 구문으로 호출된다. 하지만, 타입에서 타입 메소드를 호출 가능하고, 타입의 인스턴스에서는 불가하다.

class SomeClass {
    class func someTypeMethod() {
        // type method implementation goes here
    }
}
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
        }
    }
}

이 구조체는 모든 플레이어가 잠금 해제한 최고 레벨을 추적한다. 이 값은 highestUnlockedLevel라는 타입 프로퍼티에 저장된다.

또한 위 값과 같이 작업하는 두가지 타입 함수를 정의한다. 처음 함수는 새로운 레벨이 풀릴 때마다 값을 업데이트한다. 두번째 함수는 특정 단계가 이미 해결되었다면 true를 반환한다.

이런 타입 프로퍼티와 타입 메소드에 더해서, 각 플레이어의 게임 진행 상태를 추적한다. currentLevel로 불리는 인스턴스 프로퍼티를 사용해서 해당 플레이어의 현재 진행을 추적한다.

currentLevel 프로퍼티의 관리를 도우기 위해, advance(to:)로 불리는 인스턴스 메소드를 정의한다. 현재 단계가 업데이트되기 전에 이 메소드는 새로운 레벨이 이미 해제되었는지를 확인한다. 위 메소드는 불린값을 반환하여 실제로 현재 단계에 설정할 수 있는지 여부를 나타낸다.

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

플레이어 클래스는 플레이어의 진행을 추적하기위해 레벨트래커의 새 인스턴스를 만든다. complete(level:) 메소드를 제공하며, 이는 플레이어가 특정 단계를 완료할때마다 호출된다. 이 메소드는 모든 플레이어의 다음 단계를 풀고 플레이어가의 진행 상태를 업데이트 하여 다음 단계로 이동한다.

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 = Player(name: "Beto")
if player.tracker.advance(to: 6) {
    print("player is now on level 6")
} else {
    print("level 6 hasn't yet been unlocked")
}
// Prints "level 6 hasn't yet been unlocked"
profile
hi there 👋

0개의 댓글