예전에 자바였나? 했을 때는 함수와 메소드를 같은 거라고 보면 된다고 교수님이 가르쳐줘서 지금까지 그렇구나~ 하고 있었는데 가끔씩 메소드랑 함수가 같이 나오는 경우가 있었음.
그럴때마다 같은 게 아닌가? 라는 생각을 하긴 했었는데 진짜 다른거였음!
메소드는 바로!!
클래스, 구조체, 열거형 안에서 선언된 함수
함수긴 함순데.. 클래스, 구조체, 열거형을 곁들인..
암튼
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를 집어넣어서 아래처럼 고칠 수도 있다.
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이라서 얘네들의 프로퍼티는 기본적으로 인스턴스 메소드에서 수정할 수가 없음!
그래도 바꾸고 싶다면?
인스턴스 메소드 앞에 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 구조체의 프로퍼티를 변경할 수 있다!
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
구조체는
highestUnlockedLevel
currentLevel
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
를 통해 현재 플레이어의 스테이지를 하나 올릴 수 있음(플레이어별 개별동작).