[Swift] Property 톺아보기

문다연·2023년 1월 10일
0

ios.moon

목록 보기
24/26
post-thumbnail

① Stored Property 저장 프로퍼티

상수와 변수의 값을 인스턴스의 일부로 저장한다. 클래스와 구조체에서만 사용된다.

struct Person {
    let name: String = "John Doe" 
    var age: Int = 33
}

위와 같이 Person 구조체가 있을 때, name은 상수 저장 프로퍼티이므로 값 변경이 불가하다.
반면 age는 변수 저장 프로퍼티이므로 값을 변경해 사용할 수 있다.

⦂ Explicit Member Expression 명시적 멤버 표현식

흔히 아는 Dot Syntax를 Swift에서는 위와 같이 표현한다. 위에서 언급했듯 age는 변수 저장 프로퍼티이므로 인스턴스 생성 후 멤버인 age의 값을 dot으로 변경할 수 있다.

let p = Person()
p.age = 30

하지만p.name = "HEY"와 같이 상수 저장 프로퍼티의 값을 변경하려고 하는 것은 안된다. 오류가 발생할 것이다.
수정은 안되더라도 읽는 것은 가능하다. 동일한 방식으로 명시적 맴버 표현식을 이용하면 된다.

print(p.name)
print(p.age)

Lazy Stored Property

지연 저장 속성은 다른 포스트에서 따로 정리했다.


② Computed Property 연산 프로퍼티

프로퍼티를 사용할 때 특정 연산을 수행하여 값을 반환한다. 여기서 computed는 수학적으로 계산되는 것이 아니라, 다른 속성을 기반으로 속성값이 결정된다는 뜻이다.

저장 프로퍼티는 값을 저장할 메모리 공간을 가지는 반면, 연산 프로퍼티는 가지지 않는다. 다른 프로퍼티에 저장된 값을 읽어 연산 후 반환하는 것이다.

이런 특징때문에 프로퍼티에 접근할 때마다 다른 값이 반환될 수 있다. 그러므로 let이 아닌 var로 선언해야 한다. 연산 프로퍼티는 클래스와 구조체, 열거형에서 사용된다.

문법은 아래와 같다.

var name: Type {
	get {
    	// statements
        return value
    }
    
    set(name) {
    	// statements
    }
}

선언 시점에 기본값을 저장하지 않는다. 따라서 형식 추론이 불가능하므로 반드시 Type을 지정해주어야 한다. getter에는 return 값을 명시해야 하며, setter는 값을 저장할 때 실행된다. 괄호와 파라미터는 생략 가능하며 파라미터값은 newValue 키워드로 접근할 수 있다.

아래는 연산 프로퍼티를 활용한 예제이다.

class Person {
   var name: String
   var yearOfBirth: Int

   init(name: String, year: Int) {
      self.name = name
      self.yearOfBirth = year
   }
    
    var age: Int {
        get {
            let calendar = Calendar.current
            let now = Date()
            let year = calendar.component(.year, from: now)
            return year - yearOfBirth
        }
        
        set {
            let calendar = Calendar.current
            let now = Date()
            let year = calendar.component(.year, from: now)
            yearOfBirth = year - newValue
        }
    }
}

출생년도yearOfBirth 프로퍼티의 값을 이용하여 나이age를 계산하는 computed property를 선언하였다.

58번째 줄에서 p.age를 호출하면 p 생성자에 입력된 출생년도(year)인 2002를 기준으로 age의 get을 통해 연산된다.
60번째 줄에서 p.age의 값을 50으로 설정하고 있는데, 이때는 yearOfBirth를 이용하는 age 변수의 set 구문이 연산되면서 p.yearOfBirth가 1972가 되며, 61번째 줄 코드를 통해 확인할 수 있다.
또, 함께 작성된 주석을 보면 setter가 없으면 읽기 전용이라는 것을 알 수 있다.

⦂ Read-only Computed Property

var name: Type {
	get {
    	// statements
        return expr
    }
}

위와 같은 형태로 작성하면 읽기 전용이 되고, get 키워드와 그에 따른 괄호를 생략할 수도 있다.
⛔️ 할당 연산자를 넣게되면 클로저에 변수를 할당한다는 뜻이 되므로, 할당연산자(=)는 포함되지 않는 것을 유의하자.
⛔️ 쓰기 전용 연산 프로퍼티는 없으므로 set 구문만 쓰는 것도 불가능하다.


③ Type Property 타입 프로퍼티

class, struct, enum에서 사용된다. static 키워드로 선언하며, 자동으로 lazy하게 작동한다. (lazy 키워드를 직접 붙일 필요가 없다.)

Stored Type Property

저장 타입 프로퍼티는 항상 초기값을 가져야 한다. 그 이유는 static으로 저장되는 저장 타입 프로퍼티의 경우, 초기화할 때 값을 할당할 initializer가 없기 때문이다.

class Math {
    static let pi = 3.14
}

let m = Math()

위 예제에서 pi는 타입 프로퍼티로 선언되어 있으나, Math 클래스 내에 있고 Math의 인스턴스가 생성될 때 initializer에 의해 모든 프로퍼티가 초기화되지 않나?

타입 프로퍼티는 인스턴스가 생성될 때마다 매번 생성되는 기존 프로퍼티와 다르다. 인스턴스가 생성된다고 매번 해당 인스턴스의 멤버로 생성되는 것이 아니라, 한 번 호출되어 메모리에 올라가면 그 뒤로는 다시 호출되지 않으며 언제 어디서든 이 타입 프로퍼티에 접근할 수 있는 것이다.

m.pi // error!

따라서 pi라는 프로퍼티에 접근할 때도 위와 같이 인스턴스를 통한 방식으로는 접근할 수 없다.

Math.pi

타입 이름을 통해서만 접근이 가능하다. 앞서 타입 프로퍼티는 "한 번 호출되어 메모리에 올라가면" 이라고 언급했다. 위 예제코드에서라면 방금 Math.pi 코드를 통해 최초 호출되어 메모리에 올라갔다.
이런 방식은 lazy stored property와 동일하다고 볼 수 있다.

Computed Type Property

연산 타입 프로퍼티는 Subclass에서 오바라이딩이 가능하다. 이는 앞에 static을 붙여주느냐, class를 붙여주느냐로 구분한다.

class로 선언한 연산 프로퍼티의 경우, Subclass에서 연산 타입 프로퍼티를 오버라이드해서 사용할 수 있다.

static으로 선언한 연산 프로퍼티의 경우, 서브클래스에서 오버라이드가 불가능하다.

🤔 타입 프로퍼티는 왜 쓰는걸까 ?

보통 모든 타입이 공통적인 값을 정의하는 데 유용하기 때문이다.
위에서 쓴 수학의 파이값을 그 예로 들 수 있고, 일반적인 예로는 싱글톤이 있다.


④ Property Observer 속성 감시자

willSet 속성에 값이 지정되기 전에 호출, newValue라는 기본 파라미터가 제공된다.
didSet 값이 저장된 직후에 호출된다. 이전 값이 파라미터로 전달되며 oldValue가 기본 파라미터로 제공된다.

아래와 같이 활용할 수 있다.

class Size {
    var width = 0.0 {
        willSet {
            print(width, "=>", newValue)
        }
        didSet {
            print(oldValue, "=>", width)
        }
    }
}

let s = Size()
s.width = 123

⑤ Self & Super

self 구조체, 클래스 모두 사용한다.
super 상속과 관련있으므로 클래스에서만 사용한다.

profile
ios-moon.tistory.com 이전했어요 🚛

0개의 댓글