[Swift] 11. Method

도윤·2021년 7월 22일
0

Swift

목록 보기
11/21

Methods

메서드는 특정 타입과 연관된 함수이다. 클래스,구조체,열거형은 모두 인스턴스 메서드를 정의할 수 있고 이러한 메서드는 주어진 타입의 인스턴스는 주어진 인스턴스를 사용하기 위해 특수한 기능을 하도록 캡슐화한다.
클래스나 구조체,열거형은 메서드를 정의 할 수 있다.
c와 object-c에서는 클래스에서만 메서드를 정의할 수 있지만, swift는 클래스,구조체,열거형에서 모두 정의할 수 있다.


Instance Methods

인스턴스 메서드는 특정 클래스,구조체,열거형에 속하는 함수이다. 이러한 메서드는 인스턴스의 프로퍼티에 접근하고 수정하는 방법과 어떠한 목적과 관련된 기능을 제공하여 인스턴스 기능을 제공한다.

정의하는 방법은 일반 함수를 정의할 때와 동일한 방법으로 정의할 수 있다.
인스턴스 메서드는 인스턴스 내의 메서드나 프로퍼티에 접근할 수 있다.

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

위 클래스에서 increment,increment(by amount: Int),reset() 세가지 메서드가 정의되어 있다.

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

동일한 이름의 메서드가 정의되었지만 parameter을 통해 구분할 수 있다.

The Self Property

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

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

굳이 self를 자주 사용할 필요는 없다. self를 붙이지 않으면 swift는 메서드 내의 프로퍼티나 메서드 이름을 참조한다. 만약 메서드의 parameter와 프로퍼티의 이름이 같은 경우는 반드시 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

위 처럼 parameter:x와 프로퍼티 x를 구분하기 위해 반드시 self를 사용해야 한다.
그렇지 안흥면 모두 parameter로 처리를 한다.

Modifying Value Types from Within Instance Methods

구조체나 열거형의 Value 타입이다. 기본적으로 value type 프로펕티들은 인스턴스 메서드 내에서 수정할 수 없다. 하지만 수정을 하길 원한다면 mutating을 메서드 앞에 사용하여 수정할 수 있다.
메서드가 종료되면 메서드 내에 변화된 값들이 기존의 값으로 다시 초기화된다.메서드가 종료된 후에 새로운 프로퍼티값들이 할당되길 원한다면 self를 사용하면 된다.

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)

이렇게 프로퍼티를 수정하기 위해선 mutating키워드를 사용해야 한다.
이렇게 수정을 해줄 수 있기 때문에 이런 기능을 사용하기 위해선 구조체 타입을 변수로 선언해줘야 한다.

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

사실 self를 추가하고 안하고의 상황에서 어떠한 차이가 있는지는 잘 모르겠다.


Type Methods

위의 메서드는 각자의 인스턴스 내에서 호출가능한 메서드였지만 타입 메서드를 호출하기 위해선 static 키워드드를 메서드 func키워드 앞에 적으면서 가능해진다.

클래스에서는 class키워드로 이를 대체하여 이를 상속받은 부모클래스의 메서드를 override하여 재정의 할 수 있다.

class SomeClass {
    class func someTypeMethod() {
        // type method implementation goes here
    }
}
SomeClass.someTypeMethod()
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 메서드가 타입 메서드로 정의되어 있고 hightestUnlockedLevel은 타입 프로퍼티로 정의되어 있다.

advance 메서드는 inunlock의 반환값에 의해 curremtLevel을 수정하는 메서드이며, 구조체에서 프로퍼티의 속성값을 바꾸기 위해 mutating이 쓰여졌다.

@discardableResult속성은 함수가 값을 반환하자마자 반환값을 사용하지 않을 것이라고 알려주는 속성이다.

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:)이 호출될때마다 LevelTracker의 타입 메서그 호출된다.

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타입의 메서드를 호출하게 되고 타입 메서드가 변화시키는 값인 hightestUnlockedLevel이었으므로 모든 인스턴스에 영향을 끼친다.

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

위의 코드에서 advance(to:6)이 호출되었을 때 타입 프로퍼티 highestUnlockedLevel이 2이므로 isUnlocked가 False를 반환하므로 advance 또한 False를 반환하여서 그 결과가 위처럼 나타난다.

0개의 댓글