Methods Swift


Methods

메소드는 함수로서 특정 형식에 연계된 것이다. 클래스, 구조체, 열거형은 모두 인스턴스 메소드를 정의할 수 있는데, 주어진 형식의 인스턴스와 작동하기 위한 특정 작업과 기능성을 캡슐화한다. 클래스, 구조체, 열거형은 형식 그 자체에 대한 형식 메소드를 정의할 수 있다. 형식 메소드는 오브젝티브C내의 클래스 메소드와 비슷하다.

The fact that structures and enumerations can define methods in Swift is a major difference from C and Objective-C. In Objective-C, classes are the only types that can define methods. In Swift, you can choose whether to define a class, structure, or enumeration, and still have the flexibility to define methods on the type you create.

인스턴스 메소드

Instance methods are functions that belong to instances of a particular class, structure, or enumeration. They support the functionality of those instances, either by providing ways to access and modify instance properties, or by providing functionality related to the instance’s purpose. Instance methods have exactly the same syntax as functions, as described in Functions.

형식이 가지고 있는 열기와 닫기 브레이스 내에서 인스턴스 메소드를 작성한다. 인스턴스 메소드는 해당 형식의 다른 모든 인스턴스 메소드와 프로퍼티에 접근할 수 있다. 인스턴스 메소드는 이 것을 가진 형식의 특정 인스턴스 위에서 호출될 수 있다. 존재하는 인스턴스가 없이 고립된 곳에서는 호출될 수 없다.

단순 카운터 클래스를 정의하는 것으로서 액션이 발생한 갯수를 계산하는데 쓰이는 클래스이다.

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

The Counter class defines three instance methods:

increment() increments the counter by 1.
increment(by: Int) increments the counter by a specified integer amount.
reset() resets the counter to zero.
The Counter class also declares a variable property, count, to keep track of the current counter value.

You call instance methods with the same dot syntax as properties:

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
Function parameters can have both a name (for use within the function’s body) and an argument label (for use when calling the function), as described in Function Argument Labels and Parameter Names. The same is true for method parameters, because methods are just functions that are associated with a type.

self 프로퍼티

형식의 모든 인스턴스는 self 라 불리는 내포적 프로퍼티를 갖는데 인스턴스 자체와 정확히 동일한 것이다.You use the self property to refer to the current instance within its own instance methods.

The increment() method in the example above could have been written like this:

func increment() {
    self.count += 1
}
In practice, you don’t need to write self in your code very often. If you don’t explicitly write self, Swift assumes that you are referring to a property or method of the current instance whenever you use a known property or method name within a method. This assumption is demonstrated by the use of count (rather than self.count) inside the three instance methods for Counter.

The main exception to this rule occurs when a parameter name for an instance method has the same name as a property of that instance. In this situation, the parameter name takes precedence, and it becomes necessary to refer to the property in a more qualified way. You use the self property to distinguish between the parameter name and the property name.

Here, self disambiguates between a method parameter called x and an instance property that is also called x:

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"
Without the self prefix, Swift would assume that both uses of x referred to the method parameter called x.

인스턴스 메소드 내의 값 형식들 수정하기

Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.

However, if you need to modify the properties of your structure or enumeration within a particular method, you can opt in to mutating behavior for that method. The method can then mutate (that is, change) its properties from within the method, and any changes that it makes are written back to the original structure when the method ends. The method can also assign a completely new instance to its implicit self property, and this new instance will replace the existing one when the method ends.

You can opt in to this behavior by placing the mutating keyword before the func keyword for that method:

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)"
The Point structure above defines a mutating moveBy(x:y:) method, which moves a Point instance by a certain amount. Instead of returning a new point, this method actually modifies the point on which it is called. The mutating keyword is added to its definition to enable it to modify its properties.

Note that you cannot call a mutating method on a constant of structure type, because its properties cannot be changed, even if they are variable properties, as described in Stored Properties of Constant Structure Instances:

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

뮤테이팅 메소드내에서 self에 할당하기

뮤테이팅 메소드는 내포적인 self 프로퍼티에 완전히 새로운 인스턴스를 할당할 수 있다. 위 Point 예시는 다음과 같은 형식으로 쓰여질 수 있다.

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(x:y:)메소드는 대상 위치를 설정하는 x, y값을 갖는 새로운 구조체를 생성한다. The end result of calling this alternative version of the method will be exactly the same as for calling the earlier version.

Mutating methods for enumerations can set the implicit self parameter to be a different case from the same enumeration:

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
This example defines an enumeration for a three-state switch. The switch cycles between three different power states (off, low and high) every time its next() method is called.

타입 메소드

인스턴스 메소드는 위에 정의된데로 특정 형식의 인스턴스에서 호출되는 메소드들이다. 그 타입자체에서 호출가능한 메소드도 생성할 수 있다. 이들 메소드 종류를 타입 메소드라한다. 타입메소드는 func 키워드 앞에 static 키워드를 작성한다. 클래스는 class 키워드를 사용해 상위클래스의 구현을 상속 가능하게 할 수 있다.

일러두기)
오브젝티브C에서 형식레벨 메소드는 오브젝티브C클래스에서만 가능했다. 스위프트에서는 모든 클래스, 구조체, 그리고 열거형에서도 타입 레벨 메소드를 정의할 수 있다. 각 형식 메소드는 형식이 지원하는 범위로만 명시적으로 범위설정된다.

형식 메소드는 인스턴스 메소드처럼 닷 문법으로 호출할 수 있다. 그러나, 형식 상에서 타입 메소드를 호출하며 인스턴스에서 호출하지 못한다. SomeClass 라 불리는 클래스상의 타입 메소드를 호출하는 것을 보여준다.

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

타입메소드의 바디내에서 내포적인 self 프로퍼티는 타입의 인스턴스 보다 자신을 참조한다. 이 뜻은 타입 프로퍼티와 타입 메소드 파라미터 사이의 모호성을 제거하는데 self를 사용할 수 있는데, instance properties 와 인스턴스 메소드 파라미터일 때 이다.

더 일반적으로, 타입 메소드의 바디에서 사용하는 확인되지 않은 메소드와 프로퍼티 이름은 다른 타입 레벨 메소드와 프로퍼티를 참조할 것이다. 타입 메소드는 다른 타입 메소드를 다른 메소드 이름으로, 타입 이름을 프리픽싱할 필요없이 호출할 수 있다. 비슷하게 구조체나 열거형의 타입메소드는 타입 네임 프리픽스가 없이 타입 프로퍼티의 이름을 사용해 접근할 수 있다.

아래 예시는 LevelTracker 라는 구조체로서 게임의 다른 레벨이나 스테이즤를 통한 사용자의 진행상황을 추적하는 것이다. 이 것은 싱글플레이어 게임이지만 단일 장치에서 다중 플레이어의 정보를 저장할 수 있다.

게임이 처음 플레이되면 게임의 레벨의 모든 것은 잠겨있다. 플레이어가 레벨을 끝마치면 장치상의 모든 플레이어에 대해 레벨이 언락된다. 각 플레이어에 대한 현 레벨을 트랙한다.

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(_:)이라 불리는 형식 함수로서 새로운 레벨이 언락되는 언제든 highestUnlockedLvel 의 값을 업데이트한다. The second is a convenience type function called isUnlocked(_:), which returns true if a particular level number is already unlocked. (Note that these type methods can access the highestUnlockedLevel type property without your needing to write it as LevelTracker.highestUnlockedLevel.)

타입 프로퍼티와 타입메소드에는 추가로 LevelTracker 는 게임을 통한 각 개인 플레이어의 진행상황을 추적한다. currentLevel 이라 불리는 인스턴스 프로퍼티로 플레이어가 현재 플레이중인 레벨을 트랙한다.

currentLevel 프로퍼티를 관리하기 위해 LevelTracker 는 advance(to:) 라 불리는 인스턴스 메소드를 정의한다. currentLevel 을 업데이트하기전 이 메소드는 요청된 새로운 레벨이 언락되었는지를 체크한다. advance(to:) 메소드는 불린 값을 반환해 currentLevel 을 설정할 수 있는지 가리키는 불린값을 반환한다. Because it’s not necessarily a mistake for code that calls the advance(to:) method to ignore the return value, this function is marked with the @discardableResult attribute. For more information about this attribute, see Attributes.

The LevelTracker structure is used with the Player class, shown below, to track and update the progress of an individual player:

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
    }
}
The Player class creates a new instance of LevelTracker to track that player’s progress. It also provides a method called complete(level:), which is called whenever a player completes a particular level. This method unlocks the next level for all players and updates the player’s progress to move them to the next level. (The Boolean return value of advance(to:) is ignored, because the level is known to have been unlocked by the call to LevelTracker.unlock(_:) on the previous line.)

You can create an instance of the Player class for a new player, and see what happens when the player completes level one:

var player = Player(name: "Argyrios")
player.complete(level: 1)
print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
// Prints "highest unlocked level is now 2"
If you create a second player, whom you try to move to a level that is not yet unlocked by any player in the game, the attempt to set the player’s current level fails:

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"


덧글

댓글 입력 영역