본 내용은 스위프트 프로그래밍 3판 (야곰 지음) 교재를 공부한 내용을 바탕으로 작성 하였습니다.
프로퍼티(Property)
는 클래스, 구조체, 열거형에 관련된 값을 말한다.
프로퍼티에는 세가지의 종류가 존재한다.
저장 프로퍼티는 클래스, 구조체의 인스턴스와 관련된 값을 저장하는 프로퍼티이다. 예를 들면 변수나 상수가 있다.
var
를 사용하여 저장 프로퍼티를 선언하면 변수 저장 프로파티, let
을 사용하여 저장 프로퍼티를 선언하면 상수 프로퍼티로 선언이 되며, 변수 프로퍼티는 초기값을 변경 할 수 있으나 상수 프로퍼티는 초기값 변경(재정의)가 불가능하다.
다음은 구조체를 이용하여 저장 프로퍼티를 정의하고 초기화하는 방법이다.
struct Position {
var x: Int = 1 //저장 프로퍼티
var y: Int = 1 //저장 프로퍼티
}
var personLeePosition: Position = Position()
let personKimPosition: Position = Position(x: 5, y: 7) //이니셜라이저 사용
print("personLee의 위치: \(personLeePosition.x), \(personLeePosition.y)")
print("personKim의 위치: \(personKimPosition.x), \(personKimPosition.y)")
class NowPosition {
var point: Position //저장 프로퍼티(변수)
let personName: String //저장 프로퍼티(상수)
//이니셜라이저
init(point: Position, personName: String) {
self.point = point
self.personName = personName
}
}
let personKim: NowPosition = NowPosition(point: personKimPosition, personName: "김영희")
print(personKim.personName)
print("\(personKim.point.x), \(personKim.point.y)")
위의 Position 구조체 처럼 저장 프로퍼티 선언시 초깃값을 지정해 줄 수 있으며, 혹은 구조체에서 기본적으로 제공하는 이니셜라이저
로 프로퍼티 값을 다시 할당 할 수 있다. 아니면, NowPosition 클래스 처럼 처음부터 이니셜라이저
를 사용하여 저장 프로퍼티를 초기화 할 수 있다.
연산 프로퍼티란 값에 대한 연산이 이루어지는 프로퍼티라고 할 수 있다. 연산 프로퍼티의 구성으로 인스턴스로 부터 값을 받아 연산하여 결과를 돌려주는 접근자(getter)
와 값을 설정하는 설정자(setter)
으로 구성한다.
연산 프로퍼티의 특징 중 하나로 설정자(setter)
만을 구현하여 연산 프로퍼티를 구현 할 수 없다는 점이다. 물론 메서드를 사용하면 설정자만을 이용하여 구현하는것이 가능하다.
위의 코드에 대칭이 되는 좌표를 구하는 연산 프로퍼티를 구현해 보았다.
struct Position {
var x: Int = 1 //저장 프로퍼티
var y: Int = 1 //저장 프로퍼티
//연산 프로퍼티
var oppositePosition: Position {
get {
return Position(x: -x, y: -y)
}
set(opposite) {
x = -opposite.x
y = -opposite.y
}
}
}
var personLeePosition: Position = Position()
let personKimPosition: Position = Position(x: 5, y: 7) //이니셜라이저 사용
print("personLee의 위치: \(personLeePosition.x), \(personLeePosition.y)")
print("personKim의 위치: \(personKimPosition.x), \(personKimPosition.y)")
print(personLeePosition.oppositePosition) //대칭 좌표
personLeePosition.oppositePosition = Position(x: 10, y: 20)
//대칭좌표 재설정
print(personLeePosition)
연산 프로퍼티에서 설정자(getter)가 한줄로 구현되어 있고 반환 값이 연산 프로퍼티의 타입과 일치한다면 return
키워드를 생략해줄 수도 있다.
프로퍼티 감시자
는 프로퍼티의 값이 변경 감지 되었을때 특정 작업을 수행한다.
기본적으로 프로퍼티 감시자는 저장 프로퍼티
와 연산 프로퍼티
에서 사용할 수 있다. 다만 상속 받지 않은 연산 프로퍼티
는 프로퍼티 감시자를 사용할 수 없으며, 클래스의 상속을 받았을때 프로퍼티 감시자를 사용할 수 있다.
프로퍼티 감시자
의 구성으로 willSet
메서드와 didSet
메서드가 있다.
willSet
메서드의 newValue
에 전달되는 전달인자는 프로퍼티에 변경될 값이 전달되고 didSet
메서드의 oldValue
에 전달되는 전달인자는 프로퍼티의 변경 이전의 값이 전달된다.
다음은 Student 클래스의 상속을 받아 재정의로 연산 프로퍼티
의 프로퍼티 감시자
를 구현한 코드이다.
class Student {
var studentCount: Int = 10 {
willSet {
print("수강 인원이 \(studentCount)명에서 \(newValue)명으로 변경될 예정입니다.")
}
didSet {
print("수강 인원이 \(oldValue)명에서 \(studentCount)명으로 변경 되었습니다.")
}
}
//연산 프로퍼티
var doubleValue: Int {
get {
return studentCount
}
set {
studentCount = newValue
print("수강 인원을 \(newValue)로 변경 중입니다.")
}
}
}
class MoreStudent: Student {
override var doubleValue: Int {
willSet {
print("수강인원이 \(doubleValue)명에서 \(newValue)명으로 변경 예정입니다.")
}
didSet {
print("수강인원이 \(oldValue)명에서 \(doubleValue)명으로 변경 되었습니다.")
}
}
}
let school: MoreStudent = MoreStudent()
school.studentCount = 15
print()
school.doubleValue = 30
변수에는 클래스나 메서드, 구조체, 열거형 내부에서 접근이 가능한 지역 변수
와 전역에서 접근이 가능한 전역변수
가 존재한다.
이러한 지역변수와 전역 변수는 변수 안에 값을 저장하는 기능을 하는 저장변수
라고 한다.
또한 지역변수
와 전역변수
를 연산을 수행하는연산 변수
로 구현 할 수 있으며 뿐만 아니라 프로퍼티 감시자
도 구현 할 수 있다.
다음은 전역 변수에 연산 변수를 구현한 코드이다.
var studentName: String = "이철수"
var studentID: Int = 12345
var studentInfo: String {
get { //get 메서드로 읽기 전용 상태로 구현
"이름: \(studentName) 학번: \(studentID)" //한 줄짜리 접근자는 return 생략 가능
}
}
print(studentInfo)
지연 연산
된다. 그 자체로 지연 연산이 이루어 지므로 lazy
키워드는 생략한다. 반대로 지역변수와 지역상수는 그 차제로는 지연 연산이 되지 않는다.인스턴스의 생성과 관계 없이 모든 인스턴스가 함께 공유하고 접근하여 값을 변경 할 수 있는 프로퍼티를 타입 프로퍼티
라고한다. (다른 객체 지향 언어의 static
과 유사하다.)
타입 프로퍼티
의 종류도 두 가지로 분류되는데 저장 타입 프로퍼티
는 상수와 변수로 선언이 가능하며 연산 타입 프로퍼티
는 변수로만 선언이 가능하다.
저장 타입 프로퍼티는 반드시 초기에 초깃값을 설정해 주어야하며 지연 연산된다. 전역 변수, 전역 상수와 마찬가지로 그 자체로 지연 연산
되므로 lazy
키워드는 생략한다.
class Customer {
static let price: Double = 15000 //저장 타입 프로퍼티(상수)
static var discountRate:Double = 0 //저장 타입 프로퍼티(변수)
static var setDiscountRate: Double { //연산 타입 프로퍼티
get {
return discountRate
}
set {
discountRate = newValue
}
}
func discountPrice() -> Double {
return Self.price * Self.discountRate //Self는 타입 자체를 말함
}
}
var normalCustomer: Customer = Customer()
Customer.setDiscountRate = 0.95 //일반 고객 할인 비율 설정
print(normalCustomer.discountPrice())
var VIPCustomer: Customer = Customer()
Customer.setDiscountRate = 0.90 //VIP 고객 할인 비율 설정
print(VIPCustomer.discountPrice())
또한 위와 같이 타입 프로퍼티
는 인스턴스 생성 없이 타입 이름으로 프로퍼티를 사용 할 수 있다는 특징이 있다.