용어 정리
- 저장 프로퍼티 : 인스턴스의 변수 또는 상수 ( 클래스 , 구조체 )
- 연산 프로퍼티(get, set) : 값을 저장한 것이 아니라 특정 연산을 실행한 결괏값 ( 클래스, 구조체, 열거형 )
- 타입 프로퍼티 : 클래스 변수
- 프로퍼티 감시자 : 프로퍼티의 값이 변할 때 값의 변화에 따른 특정 작업을 수행함
저장 프로퍼티
구조체는 기본적으로 저장 프로퍼티를 매개변수로 갖는 이니셜라이저가 있음
클래스는 저장 프로퍼티의 초깃값을 지정해주지 않으면 사용자 정의 이니셜라이저를 정의해줘야 함
struct CoordinatePoint{
var x: Int // 초깃값을 지정해 줄 수도 있음
var y: Int
}
var myPoint: CoordinatePoint = CoordinatePoint(x: 10, y: 20)
class Position{
var point: CoordinatePoint // 초깂값을 지정해 줄 수도 있음
let name: String
init(name: String, currentPoing: CoordinatePoint){
self.name = name
self.point = currentPoing
}
}
let terryPosition: Position = Position(name: "terry", currentPoing: myPoint)
만약 초깃값이 있어도 되고 없어도 되는 경우 옵셔널을 사용해주면 됨
class Position{
var point: CoordinatePoint? // 초깃값을 옵셔널로 지정
let name: String
init(name: String){
self.name = name
}
}
let terryPosition: Position = Position(name: "Terry")
terryPosition.point = CoordinatePoint(x: 20, y: 10) // 선택적
초깂값을 옵셔널로 지정할때 해당 프로퍼티는 var타입이어야함 상수는 인스턴스가 생성되기 전에 초기화 되어야함
비슷한 주제로 지연 저장 프로퍼티가 있다
class Position{
lazy var point: CoordinatePoint()
let name: String
init(name: String){
self.name = name
}
}
let terryPosition: Position = Position(name: "Terry")
print(terryPosition.point) //point 프로퍼티에 접근할때 CoordinatePoint()가 처음 생성됨
연산 프로퍼티 : get, set
struct CoordinatePoint2{
var x: Int
var y: Int
var oppositePoint: CoordinatePoint2{
get{
return CoordinatePoint2(x: -x, y: -y)
}
set(opposite){
x = -opposite.x
y = -opposite.y
}
}
}
var terryPosition2: CoordinatePoint2 = CoordinatePoint2(x: 10, y: 20)
print(terryPosition2.oppositePoint) //CoordinatePoint2(x: -10, y: -20)
terryPosition2.oppositePoint = CoordinatePoint2(x: 15, y: 10) // setter
print(terryPosition2) //CoordinatePoint2(x: -15, y: -10)
struct CoordinatePoint2{
var x: Int
var y: Int
var oppositePoint: CoordinatePoint2{
get{
return CoordinatePoint2(x: -x, y: -y)
}
set{
x = -newValue.x //newValue키워드를 사용해서 파라미터를 생략가능
y = -newValue.y
}
}
}
타입 프로퍼티
- 저장 타입 프로퍼티는 변수 또는 상수로 선언 가능
- 연산 타입 프로퍼티는 변수로만 선언 가능
- 저장 타입 프로퍼티는 초깃값을 설정해야 하며 지연 연산됨( 지연 저장 프로퍼티와 다르게 다중 스레드 환경이라도 한 번만 초기화 됨을 보장 받음, lazy키워드 불필요)
class AClass {
// 저장 타입 프로퍼티
static var typeProperty: Int = 0
// 저장 인스턴스 프로퍼티
var instanceProperty: Int = 0 {
didSet {
// Self.typeProperty는
// AClass.typeProperty와 같은 표현입니다
Self.typeProperty = instanceProperty + 100
}
}
// 연산 타입 프로퍼티
static var typeComputedProperty: Int {
get {
return typeProperty
}
set {
typeProperty = newValue
}
}
}
AClass.typeProperty = 123
let classInstance: AClass = AClass()
classInstance.instanceProperty = 100
print(AClass.typeProperty) // 200
print(AClass.typeComputedProperty) // 200
타입 프로퍼티를 타입 상수로 사용할 수도 있다
class Account {
static let dollarExchangeRate: Double = 1000.0 // 타입 상수
var credit: Int = 0 // 저장 인스턴스 프로퍼티
var dollarValue: Double { // 연산 인스턴스 프로퍼티
get {
// Self.dollarExchangeRate 는
// Account.dollarExchangeRate와 같은 표현입니다
return Double(credit) / Self.dollarExchangeRate
}
set {
// Self.dollarExchangeRate는
// Account.dollarExchangeRate와 같은 표현입니다
credit = Int(newValue * Account.dollarExchangeRate)
print("잔액을 \(newValue)달러로 변경 중입니다.")
}
}
}
프로퍼티 감시자( willSet, didSet )
willSet메서드는 프로퍼티 값이 변경되기 전에 호출됨
didSet메서드는 프로퍼티 값이 변경된 직후에 호출됨
- willSet메서드의 매개변수 newValue는 프로퍼티가 변경될 값
- didSet메서드의 매개변수 oldValue는 변경되기 전의 값
- 프로퍼티를 재정의해 상속받은 저장 프로퍼티, 연산 프로퍼티에도 적용할 수 있다
- 상속받지 않은 연산 프로퍼티는 감시자를 사용할 필요도 없고 할 수도 없다 <- 당연한것
- 연산 프로퍼티는 getter, setter에서 감시자처럼 구현할 수 있다
- 연산 프로퍼티는 상속받았을 때만 프로퍼티 재정의를 통해 프로퍼티 감시자를 사용한다
- 프로퍼티 감시자는 지연 저장 프로퍼티에는 사용할 수 없다
class Account{
var credit: Int = 0{
willSet{
print("잔액이 \(credit)원에서 \(newValue)원으로 변경될 예정")
}
didSet{
print("잔액이 \(oldValue)원에서 \(credit)원으로 변경되었음")
}
}
}
클래스를 상속받아 기존의 연산 프로퍼티를 재정의하여 프로퍼티 감시자를 구현할 수 있다
연산 프로퍼티(get, set)를 재정의해도 기존의 연산 프로퍼티의 기능은 동작한다
class Account {
var credit: Int = 0 { // 저장 프로퍼티
willSet {
print("[Account] 잔액이 \(credit)원에서 \(newValue)원으로 변경될 예정입니다.")
}
didSet {
print("[Account] 잔액이 \(oldValue)원에서 \(credit)원으로 변경되었습니다.")
}
}
var dollarValue: Double { // 연산 프로퍼티
get {
return Double(credit) / 1000.0
}
set {
credit = Int(newValue * 1000)
print("[Account] 잔액을 \(newValue)달러로 변경 중입니다.")
}
}
}
class ForeignAccount: Account {
override var dollarValue: Double {
willSet {
print("[ForeignAccount] 잔액이 \(dollarValue)달러에서 \(newValue)달러로 변경될 예정입니다.")
}
didSet {
print("[ForeignAccount] 잔액이 \(oldValue)달러에서 \(dollarValue)달러로 변경되었습니다.")
}
}
}
let myAccount: ForeignAccount = ForeignAccount()
// [Account] 잔액이 0원에서 1000원으로 변경될 예정입니다.
myAccount.credit = 1000
// [Account] 잔액이 0원에서 1000원으로 변경되었습니다.
// [Account] 잔액이 0원에서 1000원으로 변경되었습니다.
// [ForeignAccount] 잔액이 1.0달러에서 2.0달러로 변경될 예정입니다.
// [Account] 잔액이 1000원에서 2000원으로 변경될 예정입니다.
// [Account] 잔액이 1000원에서 2000원으로 변경되었습니다.
myAccount.dollarValue = 2 // [Account] 잔액을 2.0달러로 변경 중입니다.
// [ForeignAccount] 잔액이 1.0달러에서 2.0달러로 변경되었습니다.
[참고링크] 프로퍼티 접근자와 감시자는 동시에 재정의 할 수 없다
'iOS > Swift' 카테고리의 다른 글
[Swift] 일급객체란? (0) | 2022.04.11 |
---|---|
[Swift] Optional(옵셔널)도 결국 Enum type(열거형 타입)이다 (0) | 2022.03.26 |
[Swift] 열거형 케이스 패턴에서 let 위치 차이점 (0) | 2022.03.25 |
[Swift] 프로퍼티 접근자와 감시자는 동시에 재정의 할 수 없다 (0) | 2022.03.17 |
[Swift] enum(열거형) (2) | 2022.02.21 |