궁금한 내용을 검색해보세요!
이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
서근 개발노트
티스토리에 팔로잉
SWIFT/Grammar

Swift : 기초문법 [메서드 #1 인스턴스 메서드, self 프로퍼티]

서근
QUOTE THE DAY

-
Written by SeogunSEOGUN

반응형

본 게시글은 yagom님과Zedd0202님의 게시글을 참고하여 작성되었습니다.

 

메서드(Method)

Class(클래스), Struct(구조체), Enum(열거형)에 포함되어있는 '함수'를  메서드라고 한다.

 

메서드는 다른 말로 클래스 함수라고도 한다.

 

Struct 는 내부에 함수를 가질 수 있으며, 이러한 함수는 필요에 따라 구조체의 프로퍼티를 사용할 수 있다. 구조체 내부의 함수는 methods 라고하지만 동일한 func 키워드를 사용한다.

 

클래스, 구조체 및 열거형은 특정 작업이나 기능을 캡슐화한 인스턴스 메서드에 타입 자체와 관련된 타입 메서드를 정의할 수 있다.

class Person { 

//이 메서드는 person 타입에만 작동(적용)됩니다.
  func personGreeting() { 
      greet(yourName: "Santosh", category: .Person)
  } 
}

클래스 안에 작성되는 함수는 이제 무조건 메서드가 된다.

func someFunction {
  //함수 구현부
}

class someClass {

   func someMethod {
   //메서드 구현부
   }

}

인스턴스 메서드 Instance Methods

인스턴스 메서드는 특정 Class, Struc 또는 Enum의 인스턴스에 속하는 함수이다. 인스턴스 프로퍼티의 접근 및 수정 방법 등을 제공하거나 인스턴스의 목적과 관련된 기능을 제공, 해당 인스턴스의 기능을 지원한다.

 

인스턴스 메서드는 함수와 달리 특정 타입 내부 { } 에 인스턴스 메서드를 작성한다. 따라서 인스턴스가 존재할 때만 사용 가능하다. 기존의 인스턴스가 없으면 호출 불가능하다.

  • 인스턴스 메서드가 속한 타입의 여는 중괄호 { 와 닫는 중괄호 } 안에 인스턴스 메서드를 작성
  • 인스턴스 메서드는, 다른 모든 인스턴스 메서드 및 해당 타입의 특성에 암시적으로(implicit) 접근
  • 인스턴스 메서드는 자신이 속한 타입의 특정 인스턴스에서만 호출될 수 있음
class Counter {
    var count = 0

    func increment() {
        count += 1
    }

    func increment(by amount: Int) {
        count += amount
    }

    func reset() {
        count = 0
    }
}

Counter 라는 클래스를 정의했고, Counter 클래스는 3가지 인스턴스 메서드를 정의하고 있다.

  • increment()는 카운터를 1씩 증가시킨다.
  • increment(By: Int)는 지정된 정수만큼 카운터를 증가시킨다.
  • reset()은 카운터를 0으로 재설정한다.

프로퍼티와 마찬가지로 . 을 통해 인스턴스 메서드를 호출할 수 있다.

let counter = Counter()// count = 0 (기본값)

counter.increment()//count = 1

counter.increment(by: 5)//count = 6

counter.reset()//count = 0

City 라는 구조체를 만들고, population에는 도시에 얼마나 많은 사람이 있는지 저장하는 프로퍼티와 인구수에 1000을 곱한 값을 반환하는 collectTaxes() 메서드가 있다고 가정하면

 

이 메서드는 City에 속하기 때문에 현재 도시의 인구 프로퍼티를 읽을 수 있게 된다.

struct City {
   var population: Int

    func collectTaxes() -> Int {
      return population * 1000
    }
} 

이 메서드는 구조체에 속하므로 다음과 같이 구조체의 인스턴스에서 호출한다.

let London = City(population: 9_000_000)
london.collectTaxes()
//9000000000

또 다른 예제를 보자면, 클래스의 인스턴스 메서드에 대한 코드이다.

class LevelClass {
    var level: Int = 0 {  //저장 프로퍼티
      //값이 변경되면 실행될 프로퍼티 옵저버
        didSet {
            print("현재 레벨: Lv.\(level)")
        }
    }
    
    func levelUp() {
        print("레벨업!")
        level += 1
    }
    
    func levelDown() {
        print("레벨 다운")
        level -= 1
        if level < 0 {
            reset()
        }
    }
    
    func jumpLevel(to: Int) {
        print("레벨이 \(to)로 점프 되었습니다.")
        level = to
    }
    
    func reset() {
        print("레벨이 초기화 됩니다.")
        level = 0
        print("레벨이 Lv.\(level)으로 초기화 되었습니다.")
    }
}

var levelClassInstance: LevelClass = LevelClass()

levelClassInstance.levelUp()
//레벨업!
//현재 레벨: Lv.1

levelClassInstance.levelDown()
//레벨 다운
//현재 레벨: Lv.0
levelClassInstance.levelDown()
//레벨 다운
//현재 레벨: Lv.-1
//레벨이 초기화 됩니다.
//현재 레벨: Lv.0
//레벨이 Lv.0으로 초기화 되었습니다.

levelClassInstance.jumpLevel(to: 20)
//레벨이 20로 점프 되었습니다.
//현재 레벨: Lv.20

위에 구현한 인스턴스 메서드에는 level 인스턴스 프로퍼티 값을 수정하는 코드가 있는데, 자신의 프로퍼티 값을 수정할 때 클래스 인스턴스 메서드는 신경 쓸 필요가 없지만, 구조체 혹은 열거형은 값 타입 이기 때문에 메서드 앞에 mutating 키워드를 반드시 사용해서 해당 메서드가 인스턴스 내부의 값을 변경한다는 것을 명시해줘야 한다.

 

 

구조체에서 속성 값을 변경하려고 할 때 나타나는 에러 메세지

struct LevelStruct {
    var level: Int = 0 {
        didSet {
            print("현재 레벨: Lv.\(level)")
        }
    }
    
    mutating func levelUp() {
        print("레벨업!")
        level += 1
    }
    
    mutating func levelDown() {
        print("레벨 다운")
        level -= 1
        if level < 0 {
            reset()
        }
    }
    
    mutating func jumpLevel(to: Int) {
        print("레벨이 \(to)로 점프 되었습니다.")
        level = to
    }
    
    mutating func reset() {
        print("레벨이 초기화 됩니다.")
        level = 0
        print("레벨이 Lv.\(level)으로 초기화 되었습니다.")
    }
}

var levelStructInstance: LevelStruct = LevelStruct()

levelStructInstance.levelUp()
//레벨업!
//현재 레벨: Lv.1

예시

struct Pokemon {
    var name: String
    func attack(with attackType: String) {
        print("\(name) uses \(attackType)!")
    }
}
struct User {
    var name: String
    var street: String
    var city: String
    var postalCode: String
    func printAddress() -> String {
        return """
        \(name)
        \(street)
        \(city)
        \(postalCode)
        """
    }
}
struct Car {
    var maxSpeed: Int
    func accelerate(to speed: Int) {
        if speed > maxSpeed {
            print("That's too fast!")
        } else {
            print("OK, let's go!")
        }
    }
}

함수와 메서드의 차이

함수를 사용하면 기능의 이름을 지정하고 반복적으로 실행할 수 있으며 Swift의 메서드는 거의 동일한 작업을 수행한다.

 

그렇다면 차이점은?

 

유일한 차이점은 메서드는 struct, enumclass 와 같은 유형에 속하지만 함수는 그렇지 않다는 것이다. 그게 유일한 차이점이다.

 

둘 다 가변 매개 변수를 포함하여 원하는 수의 매개 변수를 허용할 수 있으며 둘 다 값을 반환할 수 있다. 이 두 개는 너무 비슷해서 Swift에서는 여전히 func 키워드를 사용하여 메서드를 정의한다.

 

물론 구조체와 같은 특정 유형과 연관된다는 것은 메서드가 하나의 중요한 힘을 얻는다는 것을 의미한다. 즉, 해당 유형 내부의 다른 속성 및 메서드를 참조할 수 있다.

 

메서드 TEST: 문제를 풀려면 이곳을 클릭해주세요.

 

self 프로퍼티

타입의 모든 인스턴스는 암시적으로 생성된 self 프로퍼티를 갖는다. 이것은 인스턴스 자기 자신을 가리키는 프로퍼티이다. self 프로퍼티는 인스턴스를 더 명확히 지칭하고 싶을 때 사용하게 된다. self 프로퍼티를 사용하여 자체 인스턴스 메서드 내에서 현재 인스턴스를 참조할 수 있다.

func levelUp() {
  self.level += 1
}

Swift에서의 프로퍼티 사용 순서는 자동으로 메서드 내부에 선언된 지역변수메서드 매개변수인스턴스 프로퍼티 순으로 찾아 무엇을 지칭하는지 유추하게 된다.

class letterClass {
  var name: String = ""
  
  func sendLetter(to name: String) {
     print("\(name)에게 편지를 보냅니다.")
     self.name = name
  }
}

let Myletter: letterClass = letterClass()
Myletter.sendLetter(to: "서근")

위 코드를 보면 인스턴스 프로퍼티인 name과 매개변수로 넘어온 name의 이름이 같은데 여기서 self.name = nameself는 매개변수가 아닌 인스턴스 프로퍼티를 지칭한다. 쉽게 비교해보자면 아래와 같다.

self 프로퍼티의 다른 용도로는 값 타입 인스턴스 자체의 값을 치환할 수 있다. 클래스 인스턴스는 참조 타입이라 self 프로퍼티에 다른 참조를 할당할 수 없고, 구조체와 열거형은 값 타입이라 self 프로퍼티를 사용하여 자신 자체를 치환할 수 있다.

class LevelClass {
    var level: Int = 0
    
    func reset() {
        if level < 0 {
            self = LevelClass()  //Cannot assign to value: 'self' is immutable
        }
    }
}

↪︎ 클래스는 참조 타입이기 때문에 self 프로퍼티에 다른 참조를 할당할 수 없기에 error

struct LevelStruct {
    var level: Int = 0
    
    
    mutating func levelUp() {
        print("레벨업!")
        level += 1
    }
    
    mutating func reset() {
        print("초기화!")
        self = LevelStruct()
    }
}

var levelStructInstance: LevelStruct = LevelStruct()
levelStructInstance.levelUp() //레벨업!
print("현재 레벨 : Lv.\(levelStructInstance.level)") //현재 레벨 : Lv.1
levelStructInstance.reset()  //초기화!
print("현재 레벨 : Lv.\(levelStructInstance.level)") //현재 레벨 : Lv.0

↪︎ 구조체에서 reset 메서드의 self 프로퍼티는 LevelStruct의 기본 값이 있는 인스턴스 프로퍼티 level = 0으로 할당이 되었기 때문에 reset 메서드를 호출할 때 현재 레벨이 0이 된다.

enum OnOffSwitch {
    case on, off
    mutating func nextState() {
        self = self == .on ? .off : .on
    }
}

var toggle: OnOffSwitch = OnOffSwitch.off
toggle.nextState()
print("현재 스위치 상태 : \(toggle)")
//현재 스위치 상태 : on

↪︎ 배열 내의 nextState 메서드를 보면 삼항 연산자를 사용했는데, self(OnOffSwitch)는 self(OnOffSwitch)가 .on이라면 .Off로 타입을 변경, 그게 아니라면 .on으로 변경으로 할당해줬다.

TIP
 
 

self 인스턴스
저장 인스턴스와 매개변수 이름이 같다면 반드시 self를 붙혀 해당 타입의 저장 프로퍼티 인스터스 프로퍼티인지 구별해줘야 한다

 

 

읽어주셔서 감사합니다🤟

 

 

 


잘못된 내용이 있으면 언제든 피드백 부탁드립니다.


서근


위처럼 이미지 와 함께 댓글을 작성할 수 있습니다.