[Swift] Methods

상 원·2022년 7월 15일
0

Swift

목록 보기
19/31
post-thumbnail

예전에 자바였나? 했을 때는 함수와 메소드를 같은 거라고 보면 된다고 교수님이 가르쳐줘서 지금까지 그렇구나~ 하고 있었는데 가끔씩 메소드랑 함수가 같이 나오는 경우가 있었음.
그럴때마다 같은 게 아닌가? 라는 생각을 하긴 했었는데 진짜 다른거였음!
메소드는 바로!!

클래스, 구조체, 열거형 안에서 선언된 함수

함수긴 함순데.. 클래스, 구조체, 열거형을 곁들인..

암튼

class MyClass {
	func printSomething() {
    	print("수입푸드")
    }
}

이런식으로 선언하는 printSomething 함수가 메소드이다.

메소드도 두 종류가 있는데,

  • 인스턴스 메소드
  • 타입 메소드

한번 알아봅세다.

인스턴스 메소드

특정 구조체, 클래스, 열거형의 인스턴스에 속하는 함수

일단 몇 가지 성질이 있음!

  • 메소드가 속하는 구조체나 클래스, 열거형 타입 괄호 안에 선언
  • 해당 타입의 인스턴스 메소드나 프로퍼티에 접근 가능
  • 해당 타입을 포함해서만 호출할 수 있음

요건 Counter 클래스에 속하는 인스턴스 메소드들!

class Counter {
    var count = 0
    func increment() {
        count += 1
    }
    func increment(by amount: Int) {
        count += amount
    }
    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

self 프로퍼티

이것도 수업시간때 진짜진짜 헷갈렸던... 어디서는 self를 쓰고 어디서는 안써서 넘 어려웠다.

정의를 살펴보면!
모든 타입의 인스턴스들은 self 라는, 인스턴스 그 자체랑 동일한 암시적인 프로퍼티가 있음.
이걸 이용해서 인스턴스 메소드에서 해당 인스턴스를 참조하겠소~ 라고 하는거임!

위에서 썼던 코드를 self를 집어넣어서 아래처럼 고칠 수도 있다.

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

그렇다면 이 self 를 무조건 꼭 써줘야 되느냐?
그건 아님! self를 안 쓰면 swift가 알아서 '아~ 지금 쓰는 인스턴스에 있는 프로퍼티나 메소드를 쓰는구나~' 라고 가정하고 알잘딱깔센하게 해줌!

요게 필요할 때는 인스턴스 메소드의 매개변수 이름이 프로퍼티 이름이랑 같을때!
이때는 self를 쓰지 않으면 매개변수가 고자리에 들어가게 되고, 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"

이런식으로 self.x 와 x 를 비교하면 값이 다른 걸 알 수 있음.

인스턴스 메소드에서 Value Type 수정하기

구조체, 열거형은 value type이라서 얘네들의 프로퍼티는 기본적으로 인스턴스 메소드에서 수정할 수가 없음!
그래도 바꾸고 싶다면?
인스턴스 메소드 앞에 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)"

요런식으로 Point 구조체의 프로퍼티를 변경할 수 있다!

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

여기서 moveBy 함수는 새로운 Point 구조체를 self에다가 적용해서, 위에서 봤던 예제랑 완전 똑같은 일을 함!

enum에서도 똑같이 적용할 수 있다.

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

이건 next 메소드가 호출될 때마다 케이스가 바뀜!
Taknu 프로젝트 할 때 customTabBar 만드는 과정에서 쓰였던 것 같은 구문인데...
이걸 알았다면 좀 더 쉽게 만들 수 있었을 거 같당.

타입 메소드

타입 프로퍼티처럼 타입 그 자체의 메소드!
인스턴스를 생성해서 호출하는 메소드가 아니고 타입 그 자체의 메소드를 호출해서 쓰는 것.
예를 들어서, 인스턴스 메소드의 경우에는

struct SomeStruct {
	func printSomeThing(){
    	print("수입푸드")
    }
}
var someThing = SomeStruct()
someThing.printSomeThing()

이렇게 인스턴스를 생성해서 그 인스턴스의 메소드를 호출했지만,
타입 메소드는 인스턴스를 생성하지 않고도 타입 자체만으로 메소드 접근이 된다!

타입 메소드도 타입 프로퍼티와 동일하게 static 키워드를 사용한다.

만약에 subClass에서 오버라이딩해서 쓰고 싶은 메소드는 static 대신에 class를 쓰면 됨.

class SomeClass {
    class func someTypeMethod() {
        // type method implementation goes here
    }
}
SomeClass.someTypeMethod()

요런식으로 class를 붙이면 자식 클래스에서 오버라이딩해서 쓸 수 있다!!

static으로 붙이면 오버라이딩 안됨.

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

unlock, isUnlocked 메소드는 둘 다 LevelTracker 의 타입 메소드임.
플레이어 한 명이 스테이지를 깨면 게임을 플레이하는 나머지 플레이어에게도 동일하게 스테이지가 언락되는 게임이 있을 때,
LevelTracker 구조체는

  • unlock된 최고 스테이지를 저장하는 타입 프로퍼티 highestUnlockedLevel
  • 각 개인별 현재 스테이지를 나타내는 저장 프로퍼티 currentLevel
  • 스테이지를 unlock하는 타입 메소드 unlock
  • 스테이지가 unlock됐는지 확인하는 타입 메소드 isUnlocked
  • 플레이어별 현재 레벨을 변화시키는 메소드 advance

이런 구성으로 돼 있다.
currentLevel 로 개인별 현재 스테이지를 저장할 수 있고, 타입 프로퍼티와 메소드로는 전체를 통제할 수 있음.

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)")
// 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"

Player 클래스에 LevelTracker 구조체 인스턴스를 만들어서 개인별 진행상황을 저장할 수 있다.
플레이어가 스테이지를 클리어하면 complete 를 호출해서 LevelTracker.unlock 을 통해 언락 스테이지를 하나 올릴 수 있고(전체 언락 스테이지 하나 올라감),
tracker.advance 를 통해 현재 플레이어의 스테이지를 하나 올릴 수 있음(플레이어별 개별동작).

profile
ios developer

0개의 댓글