본문 바로가기
Swift/Language Guide

Methods - #1/1

by diosmio 2021. 9. 12.

서문

  • 메소드는 함수에 포함되는 하위개념. Class/Struct/enum에 포함되어 있는 함수를 메소드 혹은 클래스 함수라고 부른다.
  • 메소드도 인스턴스 메소드 / 타입 메소드가 있을 수 있다
  • 참고로 Objective-C를 포함한 C-like 언어에서는 Struct와 enum에서 인스턴스/타입 메소드를 만들 수 없다. Swift의 flexability

 

 

인스턴스 메소드

  1. 개념
    • 생성된 특정 인스턴스에 대한 메소드 (=생성하지 않으면 호출불가)
    • 구문형태는 Function와 완전히 동일하다
    • 어떤 인스턴스 메소드든 all other 인스턴스 메소드와 프로퍼티에 별도의 작업이나 구문없이 바로 접근할 수 있다
    • 예제
      참고로 아래 예제는 참조타입인 class이므로 바로 프로퍼티 변경 또한 가능하다
      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​
  2. Self Property
    • 모든 인스턴스는 self라는 암시적 프로퍼티를 가지고 있고 이는 인스턴스 자기 자신을 의미한다
    • 인스턴스 메소드에서 인스턴스 자신을 참조하고 싶을 때 사용될 수 있다
    • 사실 위의 예제처럼 Type 내에 정의된 프로퍼티나 메소드에 접근할 때는 self를 붙이지 않아도 Swift가 알아서 current instance에 접근함을 알 수 있다
    • 그렇지 않은 예제를 살펴보자 - self가 반드시 필요한 경우
      struct Point {
          var x = 0.0, y = 0.0
          func isToTheRightOf(x: Double) -> Bool {
              return self.x > x
      				//self없이 x라고 표현하면 parameter인지 프로퍼티인지 알 수 없음
          }
      }
      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"​
  3. 메소드에서 값타입 프로퍼티도 수정하기
    • 우선, 메소드 내에서 current instance에 접근할 수 있는 이유는 인스턴스 자기자신을 parameter(self)로 암시적 전달하기 때문이다
    • (메소드도 함수이므로) 함수에서는 전달받은 parameter가 값타입이면 copy기반이기 때문에 바꿔도 의미없고 바꿔지지도 않는다 (컴파일 에러)
    • 기존에 함수 내에서 parameter 원본값을 바꾸기 위해선 inout키워드를 사용하였다. 유사하게 메소드에서는 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))")
      // Prints "The point is now at (3.0, 4.0)"​
  4. self 바꿔버리기
    • 우리는 mutating으로 값타입인 struct와 enum에서도 프로퍼티를 변경할 수 있다
    • 놀라운건 entire 자기자신(self)조차 new Instance로 바꿀 수도 있다
      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)"​
    • 열거형에서 유용하게 사용될 것 같은 느낌이 든다
      enum TriStateSwitch {
          case off, low, high
          mutating func next() {
              switch self {
              case .off:
                  self = .low
              case .low:
                  self = .high
              case .high:
                  self = .off
              }
          }
      }​

 

 

 

타입 메소드

  1. 개념
    • 인스턴스 메소드는 어떤 타입으로 생성한 인스턴스 하나에 대해 동작하는 반면, 타입 메소드는 어떤 타입 자체에 대해 동작한다
  2. 사용
    • 일반적으로 static keyword로 사용할 수 있다
    • Swift에서 Class는 (struct와 달리) 하나 정도는 상속이 가능하다는 특징이 있다
      상속받은 자식 Class에서 타입 메소드를 overriding(재정의)하고 싶은 경우에는 static이 아닌 class로 prefix를 주어 재정의 가능여부를 구분한다
      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
          }
      		
      		//return값을 사용하지 않아도 warning을 출력하지 않는 attribute
          @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)")
      // 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"​
  3. 특징
    • 인스턴스는 타입 메소드를 호출할 수 없다
    • 타입 메소드에서의 self는 인스턴스가 아닌 타입 자체를 참조하며 타입 프로퍼티에는 이름 prefix없이 바로 접근가능하다
    • 타입 프로퍼티는 값타입인 struct/enum에서도 mutating없이 self를 변경할 수 있다. 인스턴스 프로퍼티는 명백히 값타입이지만 타입 프로퍼티는 그렇지 않은가보다

'Swift > Language Guide' 카테고리의 다른 글

Initialization - #2/2  (0) 2021.09.13
Initialization - #1/2  (0) 2021.09.13
Properties - #1/1  (0) 2021.09.12
Structures and Classes - #1/1  (0) 2021.09.12
Control Flow - #2/2  (0) 2021.09.12

댓글