[Swift 공식문서 읽기]Methods

llim🧚🏻‍♀️·2021년 8월 17일
1

Swift

목록 보기
11/26
post-thumbnail

안녕하세요. 엘림입니다🙇🏻‍♀️

Swift 공식 문서를 정독하기 시리즈입니다!

제 스타일대로 정리했으니 추가적으로 더 필요한 정보는
공식문서 링크를 눌러 확인해주세용!

좀 더 편하게 보기위해 한국어로 번역된 사이트를 함께 확인했습니다!ㅎㅎ

자, 그럼 시작해볼까요

이 글은 공부하면서 작성한 글이기 때문에 잘못된 정보가 있을 수 있습니다.🥺
금방 잊어버릴... 미래의 저에게 다시 알려주기 위한 글이다보니
혹시라도 틀린 부분이 있다면, 댓글로 친절하게 알려주시길 부탁드립니다.🙏


메소드

특정 타입의 클래스, 구조체, 열거형과 관련된 함수를 메소드라고 합니다. 특정 타입의 인스턴스에서 실행할 수 있는 메소드를 인스턴스 메소드, 특정 타입과 관련된 메소드를 타입 메소드라고 합니다.
스위프트에서는 클래스, 구조체, 열거형에서 모두 메소드를 선언해 사용할 수 있습니다.

인스턴스 메소드

인스턴스에 속한 메소드로, 인스턴스 내 값을 제어하거나 변경할 수 있습니다. 인스턴스 메소드는 그것이 속한 유형의 특정 인스턴스에서만 호출할 수 있습니다. 기존 인스턴스 없이는 별도로 호출할 수 없습니다.

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

특정 메소드에서 해당 인스턴스에 등록된 메소드나 프로퍼티를 호출하면, 현재 인스턴스의 메소드나 프로퍼티를 사용하는 것으로 가정합니다.
이 때, self를 이용해 인스터스 자체를 참조 하고 있다는 표현을 명확히 할 수 있습니다.
(모든 프로퍼티는 암시적으로 인스턴스 자체를 의미하는 self 프로퍼티를 갖습니다.)

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

위와 같은 경우에 self를 붙이고 안 붙이고의 차이가 없습니다.

struct Point {
    var x = 0.0, y = 0.0
    func isToTheRightOf(x: Double) -> Bool {
        return self.x > x  // self.x를 이용해 프로퍼티 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")
}
// "This point is to the right of the line where x == 1.0" 출력

하지만 위의 예와 같이 인자 이름이 프로퍼티 이름과 같은 경우에는, 프로퍼티에 접근하기 위해 명시적으로 self키워드를 사용해야 합니다.
(만약 프로퍼티와 인자 이름이 같을 때 self키워드를 사용하지 않으면 Swift는 자동으로 인자 이름으로 가정합니다. 또한, 인자 뿐만 아니라 내부의 변수도 마찬가지 입니다.)

인스턴스 메소드 내에서 값 타입 변경하기

구조체와 열거형은 값 타입입니다. 그래서 기본적으로 인스턴스 메소드 내에서 값 타입의 프로퍼티를 변경할 수 없습니다.
하지만 값 타입 형의 메소드에서 프로퍼티를 변경하고 싶을 때가 있을 텐데요. 어떻게 해야할까요? 바로 메소드에 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)" 출력

let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveBy(x: 2.0, y: 3.0)
// let으로 선언해서 값 변경 시도시 에러 발생!

Mutating 메소드 내에서 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)
    }
}

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

타입 메소드

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

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

타입 메소드 안에서도 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
        }
    }
}

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

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

player = Player(name: "Beto")
if player.tracker.advance(to: 2) {
    print("player is now on level 2")
} else {
    print("level 2 has not yet been unlocked")
}
// "player is now on level 2" 출력

if player.tracker.advance(to: 6) {
    print("player is now on level 6")
} else {
    print("level 6 has not yet been unlocked")
}
// "level 6 has not yet been unlocked" 출력

advance메소드 앞에 붙은 @discardableResult키워드는 리턴 값이 있는 메소드에서 리턴 값을 사용하지 않으면 컴파일러 경고가 발생하는데, 그 경고를 발생하지 않도록 해줍니다. 다시말해 xx.advance라고 하면 이 메소드를 실행하고나서 Bool값의 리턴 값을 어떤 변수에도 할당하지 않아 컴파일러 경고가 발생하지만 @discardableResult이라는 키워드를 붙여줘서 말 그대로 이 결과 값은 버릴 수 있는 결과 값이야 라고 표시를 해서 경고가 발생하지 않도록 합니다. 경고를 발생하지 않게하는 다른 방법으로는 _ = xx.advance형태로 호출하는 것도 가능합니다.


오늘도 스위프트 공식문서를 정리해보았군욥~
다음편도 힘내보겠습니다!

감사합니다🙇🏻‍♀️

profile
한달 차 iOS 개발자입니다🐥

0개의 댓글