본문 바로가기
iOS/Swift

Properties

by BenKangKang 2020. 9. 9.

Properties

특정 인스턴스와 연관있는 값

Keyword

  1. Stored Properties
  2. Lazy Stored Properties
  3. Computed Properties
  4. Property Observers
  5. Type Properties

 

1. Stored Properties

  1. Class, Struct 에서 사용 가능
  2. var 사용하면 변수 저장 프로퍼티
  3. let 사용하면 상수 저장 프로퍼티

'Stored Properties' of struct

변수 저장 프로퍼티인 name과 상수 저장 프로퍼티인 age

struct PersonStruct {
    var name: String
    let age: Int
}

var person = PersonStruct(name: "a", age: 2)
person.name = "b"
person.age = 1 // Error 발생 
  • 만약 var person을 let으로 바꾸면 어떻게 될까요?
struct PersonStruct {
    var name: String
    let age: Int
}

let person = PersonStruct(name: "a", age: 2)
person.name = "b" // Error 발생 
person.age = 1 // Error 발생 

name 부분에서 에러가 발생합니다. 그 이유는 Sturct이 Call by Value 이기 때문입니다. 레퍼런스에 접근할 수 없습니다.

'Stored Properties' of class

class PersonClass {
    var name: String
    let age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

var person = PersonClass(name: "a", age: 2)
person.name = "b"
person.age = 1 // Error 발생 
  • 위에서 실험해본 것 처럼 var person을 let으로 바꾸면 어떻게 될까요?
class PersonClass {
    var name: String
    let age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

let person = PersonClass(name: "a", age: 2)
person.name = "b"
person.age = 1 // Error 발생 

문제 없이 이름이 바뀝니다. 

2. Lazy Stored Properties

  • struct, class 에만 사용 가능
  • computed property 에는 사용할 수 없다.
  • 값이 사용되기 전 까지 계산되지 않는 프로퍼티이다.
  • 맨 앞에 lazy 라는 키워드를 붙여준다
  • 무조건 var 사용
  • 주의 할 점은 thread-safety 하지 않아서 여러번 초기화가 발생할 수 있다.
  • 지연 저장 프로퍼티를 잘 사용하면 불필요한 성능 저하나 공간 낭비를 줄일 수 있다.
class Sample {
    lazy var module = SampleModule()
}

var sample = Sample()
sample.module.run() // 이제서야 module 이 생성된다.

lazy and closure

  • lazy에 어떤 특별한 연산을 통해 값을 넣어주기 위해 코드 실행 블록인 closure를 사용한다.
  • lazy 키워드가 붙으면 closure내에서 self로 접근이 가능하다. 일반적으로는 절때 불가능하다. (당연한 이야기)
  • 클래스 내부의 클로저에서 클래스 객체를 self 로 참조한다면 메모리 누수가 발생하는 위험이 있다

아래 코드는 뒤의 ()를 통해 그 즉시 실행하고 결과를 돌려주기 때문에 메모리 누수의 걱정은 없다 (그 대신 메모리에 한번 올라간 name 이 변경되지 않는다)

class Person {
    var name:String

    lazy var greeting:String = {
        return "Hello my name is \((self.name))"
    }()

    init(name:String){
        self.name = name
    }
}

var me = Person(name:"John")

print(me.greeting // Hello my name is John

me.name = "James"

print(me.greeting // Hello my name is John

참고로 self 에 weak self 지정하면 업데이트 된 name 이 출력된다.
값이 아닌 클로저 자체가 메모리에 올라가 있고 self 는 내부에서 계속해서 클래스를 참조하기 때문에 계속 John이 출력이 되는 것이 아닌 James가 출력이 되는 것

class Person {
    var name:String

    lazy var greeting: ()->String = { [weak self] in
        return "Hello my name is \(((self?.name))!)"
    }
    init(name:String){
        self.name = name
    }
}

var me = Person(name:"John")

print(me.greeting()) // Hello my name is John

me.name = "James"

print(me.greeting()) // Hello my name is James

3. Computed Property

  • class, struct, enumeraion 모두 사용 가능하다
  • 연산 프로퍼티는 실제 값을 저장하는 프로퍼티가 아니라, 특정 상태에 따른 값을 연산하는 프로퍼티이다.
  • getter, setter 역할을 수행할 수 있다.

왜 메서드를 사용하지 않고 연산 프로퍼티를 사용하는가?

  • 프로퍼티가 메서드 형식보다 훨씬 더 간편하고 직관적이다.
  • 다만, 주의해야될 점은 연산 프로퍼티는 접근자인 get메서드처럼 읽기 전용을 구현하기 쉽지만, 쓰기 전용은 구현할 수 없다는 단점이 있다.(메서드로는 가능하지만 프로퍼티로는 불가능)

메서드 방식

struct CoordinatePoint {
    var x: Int
    var y: Int

    // 대칭점을 구하는 메서드 - 접근자(getter)
    func oppsitePoint() -> CoordinatePoint {
        return CoordinatePoint(x: -x, y: -y)
    }

    mutating func setOppositePoint(_ opposite: CoordinatePoint) {
        x = -opposite.x
        y = -opposite.y
    }
}

var position: CoordinatePoint = CoordinatePoint(x: 10, y: 20)

//현재 좌표
print(position)

//대칭 좌표
print(position.oppsitePoint())

//대칭 좌표를 (15, 10)으로 설정
position.setOppositePoint(CoordinatePoint(x: 15, y: 10))

print(position) // -15, -10

연산 프로퍼티

struct CoordinatePoint {
    var x: Int
    var y: Int

    var oppsitionPoint: CoordinatePoint {
        get {
            return CoordinatePoint(x: 10, y: 20)
        }
//        set {
//            x = -newValue.x
//            y = -newValue.y
//        }
        set (someParam) {
            x = -someParam.x
            y = -someParam.y
        }
    }
}d

4. Property Observers

  • 오로지 저장 프로퍼티에만 적용 가능하다
  • 말 그대로 프로퍼티 변경을 감시한다
  • 새로 값이 할당될때마다 호출되며 값이 같아도 호출된다.
class Account {
    var credit: Int = 0 {
        willSet {
            print("잔액이 \(credit)에서 \(newValue)원으로 변경될 예정입니다.")
        }

        didSet {
            print("잔액이 \(oldValue)에서 \(credit)으로 변경되었습니다.")
        }
    }
}

let myAccount: Account = Account()
myAccount.credit = 1000

5. Type Properties

  • 각각 인스턴스가 아닌 타입 자체에 속하게 되는 프로퍼티
  • 타입 프로퍼티는 타입 자체에 영향을 미치는 프로퍼티, 저장이나 연산은 인스턴스마다 다를 수 있다
  • 무조건 1만 리턴하는 경우

참고자료

'iOS > Swift' 카테고리의 다른 글

Struct and class  (0) 2020.09.07

댓글