Methods are functions that are associated with a particular type. Classes, structures, and enumerations can all define instance methods, which encapsulate specific tasks and functionality for working with an instance of a given type. Classes, structures, and enumerations can also define type methods, which are associated with the type itself. Type methods are similar to class methods in Objective-C.
메소드는 특정 타입과 연관된 함수입니다. 클래스, 구조체, 열거형 모두 인스턴스 메소드를 정의해 주어진 타입의 인스턴스로 작업하기 위한 특정 임무와 기능을 캡슐화 할 수 있습니다. 클래스, 구조체, 열거형은 타입 그 자체와 결합한 타입 메소드도 정의할 수 있습니다. 타입 메소드는 Objective-C
의 클래스 메소드와 비슷합니다.
스위프트는 클래스, 구조체, 열거형 모두 메소드를 정의할 수 있다. Objective-C
에선 클래스만 가능.
암시적으로 접근한다는 건 다른 인스턴스 메소드와 속성에 접근할 때
self.
를 붙이지 않아도 된다는 의미입니다.
class Counter {
var count = 0
func increment() {
count += 1
}
func increment(by amount: Int) {
count += amount
}
func reset() {
count = 0
}
}
Counter
클래스는 3가지 메소드를 정의한다.increment()
는 횟수를 1 만큼 증가increment(by : Int)
는 횟수를 지정한 정수만큼 증가reset()
은 횟수를 0 으로 재설정count
프로퍼티도 선언하였다.let counter = Counter()
// 초기 횟수는 0 임
counter.increment()
// 이제 횟수는 1 임
counter.increment(by: 5)
// 이제 횟수는 6 임
counter.reset()
// 이제 횟수는 0 임
타입의 모든 인스턴스는 인스턴스 자체를 의미하는 self
라는 암시적인 속성을 가진다. self
속성을 사용해 자신의 인스턴스 메소드 내에서 현재 인스턴스를 참조할 수 있다.
위 예제의 increment()
메소드는 다음 같이 작성할 수도 있다.
func increment() {
self.count += 1
}
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("이 점은 x == 1.0인 선의 오른쪽에 있습니다.")
}
// "이 점은 x == 1.0인 선의 오른쪽에 있습니다."를 인쇄함
isToTheRightOf(x: Double)
메소드 내부에서, self
접두사가 없다면 Swift는 두 x
모두 메소드의 매개변수를 참조한다고 가정할 것이다.구조체와 열거형은 값타입 (value types) 이다. 기본적으로 값 타입의 프로퍼티는 인스턴스 메소드 내에서 수정할 수 없다.
하지만, 프로퍼티의 값을 수정할 필요가 있다면, 함수 선언 앞에 mutating
키워드를 넣어줌으로써 프로퍼티의 값을 수정할 수 있다.
접두사에 mutating
키워드가 붙은 메소드는 메소드 안에서 자신의 프로퍼티를 바꿀 수 있으며, 어떤 방식으로 바꾸던 메소드가 끝날 때 원본 구조체 안에 다시 작성한다.
또 메소드는 자신의 암시적 self
속성에 완전히 새로운 인스턴스를 할당할 수도 있으며, 이 새 인스턴스는 메소드가 끝날 때 기존 것 대신 자리잡는다.
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, deltaY: Double) {
x += deltaX
y += deltaY
}
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
print("이제 점이 (\(somePoint.x), \(somePoint.y))에 있습니다.")
// "이제 점이 (3.0, 4.0)에 있습니다." 를 인쇄함
Point
구조체는 정해진 양 만큼 이동하는 moveBy(x:y:)
변경 메소드를 정의한다. 이 메소드는 새 점 (point) 을 반환하지 않고, 실제로 호출하는 점으로 이동한다. let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveBy(x: 2.0, y: 3.0)
// 이는 에러를 보고할 것임
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
값을 목표 위치로 설정한 새로운 구조체를 생성하여 자신의 인스턴스에 대입한다. 이 예제의 메소드 호출 결과는 앞선 버전의 호출과 정확히 일치할 것이다.
열거형의 변경 메소드는 암시적 self
매개 변수에 동일 열거형의 다른 case를 설정할 수 있다.
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 임
static
키워드를 작성해서 지정할 수 있다. 클래스의 경우 class
키워드를 사용하여 그 메소드의 슈퍼 클래스 구현을 하위 클래스가 재정의하도록 허용할 수 있다.class SomeClass {
class func someTypeMethod() {
// 타입 메소드 구현은 여기에 둠
}
}
SomeClass.someTypeMethod()
타입 메소드 안에서 암시적 self
속성은 그타입의 인스턴스가 아닌 타입 그 자체를 참조한다. 이는 인스턴스 프로퍼티와 인스턴스 메소드의 매개변수에서 처럼, 타입 인스턴스와 타입 메소드의 매개변수를 헷갈리지 않게 하는데 self
를 사용할 수 있다는 의미이다.
더 일반적으로, 타입 메소드는 타입 이름을 접두사로 붙이지 않은 메소드 이름을 가지고 또 다른 타입 메소드를 호출할 수 있다. 이와 비슷하게, 구조체와 열거형에 대한 타입 메소드 역시 타입 이름 접두사를 붙이지 않고 타입 프로퍼티 이름으로 타입 프로퍼티에 접근이 가능하다.
아래의 예제는 게임의 레벨 또는 단계별 진행 사항을 추적하는 LevelTracker
라는 구조체를 정의한다. 이 게임은 1인용 게임이지만 여러 플레이어의 정보를 단일 장치에 저장할 수 있다.
게임을 처음 플레이할 때 1단계 레벨을 제외한 모든 게임 레벨이 잠긴다. 플레이어가 레벨을 완료할 때마다 해당 레벨은 장치의 모든 플레이어에서 잠금 해제가 된다. 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
구조체는 어떤 플레이어가 레벨을 완료했던 가장 높은 레벨을 추적한다. 이 값은 highestUnlockedLevel
라는 타입 프로퍼티에 저장된다.
또 LevelTracker
는 highestUnlockedLevel
프로퍼티와 작업하기 위한 두가지의 타입 메소드를 정의한다.
highestUnlockedLevel
프로퍼티 값을 갱신할 unlock(_:)
타입 메소드true
를 반환하는 isUnlocked(_:)
라는 편의를 위한 타입 메소드위 두개의 타입 메소드에서 highestUnlockedLevel
타입 프로퍼티에 접근하기 위해 LevelTracker.highestUnlockedLevel
와 같이 타입 접두사를 작성하지 않는 것을 기억하자.
자신의 타입 속성과 타입 메소드에 더해, LevelTracker
는 개별 플레이어의 게임 진행 상황도 추적한다. 이는 currentLevel
라는 인스턴스 프로퍼티로 플레이어의 게임 진행 상황을 추적한다.
currentLevel
프로퍼티의 관리를 돕기위해, LevelTracker
는 advance(to:)
라는 인스턴스 메소드를 정의한다. advance(to:)
메소드는 currentLevel
을 업데이트하기 전에 새로 요청한 레벨이 이미 완료되었는지 검사한다. advance(to:)
메소드는 currentLevel
값을 설정하는 것이 실제로 가능한지 지시하는 불리언 값을 반환한다.
advance(to:)
메소드를 호출하는 코드가 반환 값을 무시하는 것은 항상 실수가 되는 것이 아니므로, 이 함수는 @discardableResult
특성으로 표시된다.
@discardableResult
특성으로 표시되면 메소드가 반환값을 가지고 있고, 그 반환값을 사용하지 않더라도 컴파일러가 경고를 주지 않는다.
class Player {
var tracker = LevelTracker()
let playerName: String
init(name: String) {
playerName = name
}
func complete(level: Int) {
LevelTracker.unlock(level + 1)
tracker.advance(to: level + 1)
}
}
Player
클래스는 플레이어의 진행상황을 추적하기 위해 LevelTracker
인스턴스를 생성한다. 또 플레이어가 특정 레벨을 완료했을 때마다 호출되는 complete(level:)
메소드를 제공한다. 이 메소드는 모든 플레이어에 대한 다음 레벨을 언락하고 플레이어의 진행상황을 갱신하여 그 다음 레벨로 이동한다.advance(to:)
의 불리언 반환 값은 무시한다. 이는 이전 줄에서 LevelTracker.unlock(_:)
을 호출함으로써 레벨이 완료된 것을 이미 알고 있기 때문이다.Player
클래스 인스턴스를 생성하고, 플레이어가 레벨을 완료하였을 때 무슨일이 일어나는지 보면:var player = Player(name: "Wonbi")
player.complete(level: 1)
print("언락된 가장 높은 레벨은 \(LevelTracker.highestUnlockedLevel)입니다.")
// Prints "언락된 가장 높은 레벨은 2입니다."
player = Player(name: "Beto")
if player.tracker.advance(to: 6) {
print("현재 플레이어의 레벨은 6입니다.")
} else {
print("레벨 6은 아직 언락되지 않았습니다.")
}
// Prints "레벨 6은 아직 언락되지 않았습니다."