[ETC_011] 프로퍼티와 메서드 (2)
👨🏻‍💻iOS 공부/Swift 기본기 다지기

[ETC_011] 프로퍼티와 메서드 (2)

728x90
반응형

키 경로와 메서드

키 경로


(신속한,재빠른을 의미하는 영단어인 Swift! 로고는 Swift라고 불리우는 칼새를 의미한다고 한다...!)




키 경로(Key Path)란 무엇일까?
보통은 struct.property 나 class.property와 같이 일반적으로 접근하나, 키 경로의 경우 간접적으로 특정 타입의 어떤 프로퍼티 값을 가리켜야 할지 미리 지정해두고 사용할 수 있다.

\타입이름.경로.경로 ~

일반적으로 위 처럼 사용되며, 예시를 통해 이해해보자.

import UIKit


class Person {

    var name: String

    

    init(name:String){

        self.name = name

    }

}


struct Product {

    var name: String

    var owner: Person

}


print(type(of: \Person.name)) // ReferenceWritableKeyPath<Person, String>  : class(참조 타입)에 키 경로 타입으로 읽고 쓸 수 있다.

print(type(of: \Product.name)) // WritableKeyPath<Product, String>  : 값 타입에 키 경로 타입으로 읽고 쓸 수 있다.


// 기존의 키 경로에 하위 경로를 덧붙이기

let keyPath = \Product.owner

let nameKeyPath = keyPath.appending(path: \.name)  // \Product.owner.name과 동일


// 실 사용법

let cha = Person(name: "chamin")

let macbook = Product(name: "cha_mac", owner: cha)


// 키 경로 사전 정의

let realKeyPath = \Product.owner.name

// 프로퍼티 반환

macbook[keyPath: realKeyPath] // "chamin"

// 프로퍼티 값 변경

macbook[keyPath: realKeyPath] = "new_owner"

macbook.owner.name // new_owner

약간 파이썬 딕셔너리 느낌이다. 키 경로를 미리 정의해놓고 이를 활용해서 프로퍼티에 접근하여 값을 얻어나 변경할 수 있다.
키 경로를 잘 활용하면 프로토콜과 마찬가지로 타입 간의 의존성을 낮추는 데 많은 도움이 된다고 하니 알아두면 좋을 것 같다!

메서드

메서드란, 특정 타입에 관련된 함수를 말한다. Swift에서는 구조체와 열거형이 메서드를 가질 수 있다는 것이 타 프로그랠밍 언어와의 가장 큰 차이점이다.

인스턴스 메서드

인스턴스 메서드는 특정 타입의 인스턴스에 속한 함수를 뜻한다. 내부 프로퍼티 값을 변경하거나, 특정 연산 결과를 반환하는 등 인스턴스와 관련된 기능을 수행한다고 한다.
함수와 문법이 같다고 하나, 인스턴스가 존재해야 인스턴스 메서드를 사용할 수 있다는 것이 차이점이다.

코드를 봐보자.

class testGame{

    var level: Int = 0 {

        didSet {

            print("Level : \(level)")

        }

    }

    

    func levelUp() {

        print("Level Up!!")

        level += 1

    }

    

    func leveldown() {

        print("Level Down")

        level -= 1

        if level < 0 {

            reset()

        }

    }

    

    func reset() {

        print("Dead! again level: 0")

        level = 0

    }

    

    func cheatKey(_ new_level: Int) {

        level = new_level

    }

}


var myLevel: testGame = testGame()


myLevel.levelUp() // Level UP!! level = 1

myLevel.leveldown() // Level Down   level = 0

myLevel.leveldown() // Dead! again level: 0

myLevel.cheatKey(10) // level: 10


중간에 보면 myLevel 인스턴스 프로퍼티 값을 변경하는 코드가 있다. 클래스의 경우는 참조 타입이기에 인스턴스 메서드 사용시 크게 신경쓰지 않아도 되나, 구조체나 열거형과 같은 값 타입에는 메서드 사용시 앞에mutating을 적어줘야 한다. mutating키워드를 붙여서 메서드가 인스턴스 내부의 값을 변경하겠다는 것을 명시해줘야 한다.

struct customGame {

    var difficulty: String = "normal" {

        didSet {

            print("game difficulty is changed : \(difficulty)")

        }

    }

    

    mutating func diffUp() {

        print("difficulty to hard!")

        difficulty = "Hard"

    }

    

    mutating func diffDown() {

        print("difficulty to easy!")

        difficulty = "easy"

    }

    

    mutating func diffCustom(_ to: String) {

        difficulty = to

    }

}


var PESdiff: customGame = customGame()


PESdiff.diffUp() // difficulty to hard!

PESdiff.diffDown() // difficulty to easy!

PESdiff.diffCustom("Extreme")

구조체, 열거형 등 값 타입 사용시 내부 인스턴스 프로퍼티 값 변경시, mutating 키워드만 까먹지 말자!


self 프로퍼티

모든 인스턴스는 암시적으로 생성된 self 프로퍼티를 갖는다. 이는 인스턴스 자기 자신을 가리키는 프로퍼티이다. self 프로퍼티의 경우 인스턴스를 더 명확히 지칭하고 싶을 때 주로 사용한다. 인스턴스 프로퍼티와 매개변수의 이름이 같다면? 이럴 때 self를 붙여 사용해준다.


class game {

    var name: String = "User"

    

    func newName(name: String) {

        self.name = name

    }

}

그냥 name = name으로 적게 되면, 좌측 name을 매개변수로 들어온 것으로 인식을 하게 된다. 인스턴스 프로퍼티 값을 변경하기 위해 사용한 것인데 매개변수로 인식하면 안되기에, self를 붙여서 인스턴스 프로퍼티라는 것을 명확히 해준다!

추가적으로 self 프로퍼티의 다른 용도는 값 타입(구조체, 열거형) 인스턴스 자체의 값을 치환할 수 있다는 것이다. 클래스 인스턴스의 경우 참조 타입이라 self 프로퍼티에 다른 참조를 할당할 수 없는데, 값 타입의 경우는 자신 자체를 치환할 수 있다. 

struct Level {

    var level: Int = 0

    

    mutating func levelUP() {

        level += 1

    }

    mutating func levelChange() {

        print("Reset")

        self = Level()

    }

}


var playLevel: Level = Level()

playLevel.levelUP()

playLevel.levelChange() // reset


타입 메서드

앞에서 인스턴스, 타입 프로퍼티를 봤듯이 타입 메서드도 존재한다. 마찬가지로 static 키워드를 붙여서 타입 자체에 호출이 가능한 타입 메서드임을 나타내준다.

다만 static func로 정의하면 상속 후 메서드 재정의가 불가능하고, class func로 정의하면 상속 후 메서드 재정의가 가능하다!

class AClass {

    static func staticTypeMethod() {

        print("staticTypeMehod")

    }

    

    class func classTypeMethod() {

        print("classTypeMethod")

    }

}


class BClass: AClass {

    /*

     // 에러 메시지 : cannot override static method (재정의 불가)

    override static func staticTypeMethod() {

        

    }

    */

    override class func classTypeMethod() {

        print("override possible")

    }

}


그리고 타입 메서드는 인스턴스 메서드와 달리 self 프로퍼티가 "타입 그 자체"를 가리킨다는 점이 다르다. 인스턴스 메서드에서는 self가 인스턴스를 가리켰지만, 타입 메서드의 self는 "타입"을 가리킨다. 그래서 타입 메서드에서 self 프로퍼티를 사용하면 타입 프로퍼티 및 타입 메서드를 호출할 수 있다. 


언택트 시대에 적합하게 화상회의 할 때를 생각해보자. 자, Zoom에 접속했다. 가장 먼저 해야할 일은..? 마이크를 음소거 하고 비디오를 끈다...ㅋㅋ

노트북 시스템 상 마이크 소리는 들어가겠지만, Zoom내에서 마이크를 통제할 수 있다. 계속 음소거 하고 있을 순 없으니, 발표할 차례가 오면 다시 음소거를 해제해준다. 이 상황을 코드로(?) 타입 프로퍼티와 타입 메서드, self 프로퍼티를 사용해서 구현해보자!


// 언택트 시대에 알맞게 화상 회의에서 발언할 때에 마이크를 키고, 이외에는 끌 수 있도록 해보자.

struct SystemVol {

    // 타입 프로퍼티를 사용하여 유일한 값으로 만들어준다.

    static var volume: Int = 5

    

    // 타입 메서드를 사용해서 타입 프로퍼티를 제어해준다.

    static func mute() {

        self.volume = 0 // SystemVol.volume = 0이랑 동일하다.

    }

}


// 자 회의를 시작해보자!

class WeeklyMeeting {

    

    // 회의 내 음량 조절

    var volume: Int = 10

    

    // 다른 사람이 말할 땐 음소거!

    func listening() {

        SystemVol.mute()

    }

    

    // 내가 말할 차례!

    func speaking() {

        // 원래 음량으로 복구

        SystemVol.volume = self.volume

    }

}


SystemVol.volume // 5


let myMeeting: WeeklyMeeting = WeeklyMeeting()


myMeeting.listening()

print(SystemVol.volume) // 0

myMeeting.speaking()

print(SystemVol.volume) // 10  : WeeklyMeeting내 음량으로 설정


처음에 접속 후 음소거를 하지 않으면 초깃값인 5가 반환되게 된다. 

이에 헐레벌떡 음소거 처리를 해주면 시스템 볼륨이 0이 되고, 발표차례가 와서 WeeklyMeeting내 volume 인스턴스의 값을 가져와 음량을 복구할 수 있다! 


이렇게 프로퍼티와 메서드에 대해서 알아보았다. 

핵심적이고 중요한 부분이고, 계속 사용될 예정이기에 반복학습하여 몸에 익혀야겠다! 


끄읕.


ko-fi

728x90
반응형