Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

메서드 #47

Open
simoniful opened this issue Oct 25, 2022 · 0 comments
Open

메서드 #47

simoniful opened this issue Oct 25, 2022 · 0 comments

Comments

@simoniful
Copy link
Owner

simoniful commented Oct 25, 2022

메서드는 특정 타입과 연관된 함수
클래스, 구조체, 열거형은 인스턴스 메서드를 정의 가능
메서드를 통해 주어진 타입의 인스턴스와 작동하는 기능 또는 특정한 업무를 캡슐화한다

클래스, 구조체, 열거형은 타입 그 자체와 연관된 타입 메서드 역시 정의 가능
타입 메서드는 Objective-C의 클래스 메서드와 유사

Swift에서 구조체와 열거형이 메서드를 정의할 수 있다는 사실은
클래스만이 메서드를 정의할 수 있는 C, Objective-C와의 중요한 차이점

인스턴스 메서드 (Instance Methods)

인스턴스 메서드(instance method)는 특정 클래스, 구조체, 열거형의 인스턴스에 속한 함수
인스턴스 프로퍼티에 접근하고 수정하는 방식을 제공하거나,
인스턴스의 목적과 관계된 기능을 제공함으로써 인스턴스의 기능을 지원
인스턴스 메서드는 함수와 정확히 같은 문법으로 구성

인스턴스 메서드는 타입 내부의 다른 인스턴스 메서드와 프로퍼티에 암시적으로 접근할 수 있다
인스턴스 메서드는 그것이 속한 타입의 특정 인스턴스에서만 호출될 수 있으며, 존재하는 인스턴스 없이 고립되어 호출될 수는 없다

// Counter 클래스는 세 가지 인스턴스 메서드와 count 변수 프로퍼티를 정의
class Counter {
    var count = 0

    // increment() : count를 1 증가
    func increment() {
        count += 1
    }

    // increment(by: Int) : count를 특정 정수만큼 증가
    // 함수 매개 변수는 이름과 인자 라벨을 모두 가질 수 있다
    // 메서드는 단순히 타입과 연관되어 있는 함수기 때문에 동일하게 작동
    func increment(by amount: Int) {
        count += amount
    }

    // reset() : count를 0으로 되돌림
    func reset() {
        count = 0
    }
}

// 점 문법을 사용하여 인스턴스 메서드를 호출
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

1) self 프로퍼티

모든 타입의 인스턴스는 self라 불리는 암시적 프로퍼티를 가지고 있다
프로퍼티는 인스턴스 그 자신과 동일
self 프로퍼티를 인스턴스 메서드 안에서 현재 인스턴스를 참조하기 위해 사용 가능

increment() 메서드는 다음과 같이 작성 가능

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

실제로는 self를 자주 작성할 필요가 없음
명시적으로 self를 작성하지 않는다면, Swift는 메서드 안에서 외부에서 선언된 이름의 프로퍼티나 메서드를 사용할 때
현재 인스턴스의 프로퍼티나 메서드를 참조하는 것으로 가정

인스턴스 메서드를 위한 매개 변수 이름과 인스턴스의 프로퍼티 이름이 같을 때는 예외
매개 변수 이름이 우선권을 가지며, 프로퍼티를 우선적으로 참조하는 방법에 대한 필요성이 생긴다
self 프로퍼티를 사용한다면 매개 변수 이름과 프로퍼티 이름을 구별 가능

struct Point {
    var x = 0.0, y = 0.0
    func isToTheRightOf(x: Double) -> Bool {
        // self 접두사가 없다면 Swift는 두 x를 모두 매개 변수 이름의 x로 가정
        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"

2) 인스턴스 메서드에서 값 타입 수정하기

구조체와 열거형은 값 타입
기본적으로, 값 타입의 프로퍼티는 인스턴스 메서드 안에서 수정 불가

만약 특정 메서드 안에서 구조체나 열거형의 프로퍼티를 수정해야 할 경우, 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)"

상수 구조체에는 변경 메서드(mutating method)를 호출할 수 없다
변수 프로퍼티일지라도 변경될 수 없기 때문

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

3) mutating 메서드 안에서 self에 할당하기

mutating 메서드는 새로운 인스턴스를 암시적인 self 프로퍼티에 할당 가능

struct Point {
    var x = 0.0, y = 0.0
    // mutating 메서드 moveBy(x:y:)는 타겟 로케이션으로 설정된 x와 y 값을 지니는 새로운 구조체를 생성
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        self = Point(x: x + deltaX, y: y + deltaY)
    }
}

열거형의 mutating 메서드는 암시적 self 매개 변수에 같은 열거형의 다른 케이스를 설정 가능

enum TriStateSwitch {
    case off, low, high
    
    // next() 메서드 호출을 통한 순환식 변경 구현
    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 Method)

인스턴스 메서드는 특정 타입의 인스턴스에서 호출하는 메서드와 달리,
타입 자체에서 호출하는 타입 메서드(type method)도 정의할 수 있다
메서드의 func 키워드 이전에 static 키워드를 작성하여 타입 메서드임을 알린다
클래스의 경우, 자식 클래스가 부모 클래스의 메서드 구현을 오버라이드하는 것을 허용하기 위해 class 인스턴스를 사용할 수도 있다

Objective-C에서 타입 레벨 메서드는 오직 Objective-C 클래스로만 정의 가능했다
Swift에서는 모든 클래스, 구조체, 열거형에 대해 타입 레벨 메서드를 정의 가능하며,
각 타입 메서드는 명시적으로 그 타입이 지원하는 범위에 맞춰진다

타입 메서드는 다른 인스턴스 메서드처럼 점 문법으로 호출
하지만, 인스턴스가 아닌 타입 자체에서만 타입 메서드를 호출할 수 있다

class SomeClass {
    // 클래스의 경우 class 키워드로 선언이 가능 - 오버라이딩
    class func someTypeMethod() {
        // type method implementation goes here
    }
}

SomeClass.someTypeMethod()

타입 메서드의 바디에서, 암시적인 self 프로퍼티는 타입의 인스턴스가 아니라 타입 그 자체를 참조
인스턴스 프로퍼티와 인스턴스 매개 변수에서 그랬던 것처럼
타입 프로퍼티와 타입 메서드 매개 변수를 명확히 구분하기 위해 self를 사용할 수 있다

보다 일반적으로 타입 메서드의 본문 내에서
사용하는 정규화되지 않은 메서드 및 프로퍼티 이름은
다른 타입 레벨의 메서드 및 프로퍼티을 참조

타입 메서드는 타입 이름으로 접두사를 붙일 필요 없이 다른 타입의 메서드를 호출할 수 있다
마찬가지로 구조체 및 열거의 형식 메서드는 타입 이름 접두사 없이 타입 프로퍼티의 이름을 사용하여 타입 프로퍼티에 액세스 가능

// LevelTracker 구조체는 게임의 다양한 레벨 또는 단계를 통해 플레이어의 진행 상황을 추적
// 1인용 게임이지만 여러 플레이어의 정보를 단일 장치에 저장한다고 가정
// 게임의 모든 레벨(레벨 1을 제외한)은 게임이 처음 실행될 때 잠김
struct LevelTracker {
    // highestUnlockedLevel 타입 프로퍼티에 모든 플레이어가 잠금 해제한 가장 높은 레벨을 추적 저장
    static var highestUnlockedLevel = 1
    // currentLevel 인스턴스 프로퍼티를 사용하여 플레이어가 현재 플레이 중인 레벨을 확인
    var currentLevel = 1

    // 새로운 레벨이 풀릴 때마다 highestUnlockedLevel 값을 업데이트 
    // LevelTracker.highestUnlockedLevel라고 명시할 필요 없이 엑세스 가능
    static func unlock(_ level: Int) {
        if level > highestUnlockedLevel { highestUnlockedLevel = level }
    }

    // 특정 레벨이 이미 풀렸을 경우 true를 반환
    // LevelTracker.highestUnlockedLevel라고 명시할 필요 없이 엑세스 가능
    static func isUnlocked(_ level: Int) -> Bool {
        return level <= highestUnlockedLevel
    }

    // 플레이어가 레벨을 완료할 때마다 해당 레벨은 장치의 모든 플레이어에서 잠금 해제
    // currentLevel가 업데이트 되기 전에, 이 메소드는 요구되는 새로운 레벨이 이미 풀렸는지를 확인
    // currentLevel을 설정하는 게 가능한지 불가능한지를 Boolean 값으로 반환
    // 메서드를 호출하는 코드가 반환 값을 사용하지 않을 수도 있기에, @discardableResult 속성을 표시
    @discardableResult
    mutating func advance(to level: Int) -> Bool {
        if LevelTracker.isUnlocked(level) {
            currentLevel = level
            return true
        } else {
            return false
        }
    }
}

class Player {
    // 해당 플레이어의 진행도를 추적하고 업데이트 하기 위해 LevelTracker의 새 인스턴스를 생성
    var tracker = LevelTracker()
    let playerName: String

    // 플레이어가 특정 레벨을 완료할 때마다 호출되는 complete(level:) 메서드를 제공
    // 모든 플레이어의 다음 레벨을 잠금 해제하고 플레이어의 진행률을 업데이트하여 다음 레벨로 이동
    func complete(level: Int) {
        LevelTracker.unlock(level + 1)
        // LevelTracker.unlock(_:) 호출로 레벨이 잠금 해제된 것으로 알려져 있기 때문에 advance(:)의 Bool 반환 값은 무시
        tracker.advance(to: level + 1)
    }

    init(name: String) {
        playerName = name
    }
}

// 새 플레이어에 대한 플레이어 클래스 인스턴스를 만들고 플레이어가 레벨 1을 완료
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 has not yet been unlocked")
}
// Prints "level 6 has not yet been unlocked"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant