일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- iOS계산기
- 자바스크립트
- FLASK
- 웹
- AJAX
- Python
- 스위프트
- 맥
- Xcode
- customclass
- 앱배포
- iOS배포
- subscript
- 파이썬서버
- JavaScript
- FileOwner
- Xib
- Swift
- 계산기앱만들기
- MainScheduler
- iOS앱배포
- 딩동말씀
- 스프링
- 앱버전구하기
- ios
- 개발기록
- spring
- jQuery
- 계산기앱
- DispatchGroup
- Today
- Total
개발하는 뚝딱이
[swift] 프로퍼티(property) 본문
책 <스위프트 프로그래밍 3판 ;야곰 지음> 을 정리한 글입니다.
프로퍼티
1. 저장 프로퍼티
2. 지연 저장 프로퍼티
3. 연산 프로퍼티
4. 프로퍼티 감시자
5. 타입 프로퍼티
6. 키 경로
프로퍼티
- 클래스, 구조체, 열거형 등에 관련된 값을 의미
- 저장 프로퍼티 / 연산 프로퍼티 / 타입 프로퍼티
- 프로퍼티 감시자(Property Observers)를 이용하여 값이 변하는 것을 감시
1-0. 저장 프로퍼티 (Stored Property)
- var / let 사용가능
- let 키워드를 사용한 상수 프로퍼티는 값을 재할당할 수 없음
- struct는 저장 프로퍼티를 모두 포함하는 이니셜라이저를 자동으로 생성
- class는 저장 프로퍼티가 옵셔널이 아니면 기본값을 저장하거나, 사용자 정의 이니셜라이저를 통해 반드시 초기화해야 함
struct CoordinatePoint {
var x: Int // 저장 프로퍼티
var y: Int // 저장 프로퍼티
}
let point1: CoordinatePoint = CoordinatePoint(x: 10, y: 5)
class Position {
let name: String // 저장 프로퍼티
var point: CoordinatePoint // 저장 프로퍼티
init(name: String, currentPoint: CoordinatePoint) { // 이니셜라이저 필수
self.name = name
self.point = currentPoint
}
}
let point2: Position = Position(name: "Jin", currentPoint: point1)
1-1. 저장 프로퍼티의 초깃값 지정
- 프로퍼티의 초깃값을 할당하면, 이니셜라이저 전달인자로 초깃값을 넘길 필요가 없음
struct CoordinatePoint {
var x: Int = 2 // 저장 프로퍼티
var y: Int = 3// 저장 프로퍼티
}
let point1: CoordinatePoint = CoordinatePoint(x: 10, y: 5)
let point2: CoordinatePoint = CoordinatePoint()
class Position {
var name: String = "James" // let으로 선언하면 값 변경이 안되므로 var 키워드 사용
var point: CoordinatePoint
init(currentPoint: CoordinatePoint) {
self.point = currentPoint
}
}
let point2: Position = Position(currentPoint: point1)
point2.name = "Jin"
- 옵셔널로 타입을 선언하면 이니셜라이징하지 않아도 됨
struct CoordinatePoint {
var x: Int
var y: Int
}
class Position {
let name: String
var point: CoordinatePoint?
init(name: String) {
self.name = name
}
}
let jinPosition: Position = Position(name: "Jin")
jinPosition.point = CoordinatePoint(x: 20, y: 5)
2-0. 지연 저장 프로퍼티 (lazy stored property)
- 호출이 있어야 값을 초기화
- 상수는 인스턴스가 완전히 생성되기 전에 초기화해야 함 → let 이 아닌 var 키워드 사용 → lazy var
- 주로 복잡한 클래스나 구조체 구현 시 사용
struct CoordinatePoint {
var x: Int = 0
var y: Int = 0
}
class Position {
lazy var point: CoordinatePoint = CoordinatePoint()
let name: String
init(name: String) {
self.name = name
}
}
let position1: Position = Position(name: "Jin")
position1.point = CoordinatePoint(x: 3, y: 10) // point 프로퍼티로 처음 접근하므로 CoordinatePoint가 생성됨
3-0. 연산 프로퍼티 (Computed Property)
- 실제 값을 저장하는 프로퍼티가 아니라, 특정 상태에 따른 값을 연산하는 프로퍼티
- setter, getter의 역할 가능
- 메소드 대신 연산 프로퍼티를 쓰는 이유 ; 인스턴스 외부에서 메서드를 통해 내부 값에 접근하려면 getter, setter 두개를 구현해야 함. 또한 코드의 가독성을 고려했을 때 연산 프로퍼티가 훨씬 직관적
- 그러나 연산 프로퍼티 구현 시, getter만 O / getter, setter 둘 다 O / setter만 X
- 관용적으로 setter 연산프로퍼티에서 newValue 사용
struct CoordinatePoint {
var x: Int = 0
var y: Int = 0
// getter 메서드
func oppositePoint() -> Self { // Self 대신 CoordinatePoint 사용 가능
return CoordinatePoint(x: -x, y: -y)
}
// setter 메서드
mutating func setOppositePoint(_ opposite: CoordinatePoint) {
x = -opposite.x
y = -opposite.y
}
}
var position1: CoordinatePoint = CoordinatePoint(x: 10, y: 20)
print(position1) // 10, 20
print(position1.oppositePoint()) // -10, -20
position1.setOppositePoint(CoordinatePoint(x: 10, y: 15))
print(position1) // -10, -15
struct CoordinatePoint {
var x: Int = 0
var y: Int = 0
var oppositePoint: CoordinatePoint {
// getter
get {
return CoordinatePoint(x: -x, y: -y)
}
// setter
set(newValue) {
x = -newValue.x
y = -newValue.y
}
}
}
var position1: CoordinatePoint = CoordinatePoint(x: 10, y: 20)
print(position1) // 10, 20
print(position1.oppositePoint) // -10, -20
position1.oppositePoint = CoordinatePoint(x: 20, y: 15)
print(position1) // -20, -15
4. 프로퍼티 감시자
- 프로퍼티의 값이 새로 할당될 때마다 호출
- 일반 저장 프로퍼티, 재정의해 상속받은 저장 프로퍼티, 상속받은 연산 프로퍼티 사용 가능 (지연 저장 프로퍼티 사용 불가)
- willSet 메서드 : 값이 변경되기 직전에 호출. 매개변수(newValue)는 변경될 값으로 자동 저장
- didSet 메서드 : 값이 변경된 직후에 호출. 매개변수(oldValue)는 변경되기 전 값으로 자동 저장
class Account {
var credit: Int = 0 { // 저장 프로퍼티
willSet {
// [2]
print("잔액이 \(credit)원에서 \(newValue)원으로 변경될 예정입니다.")
}
didSet {
// [3]
print("잔액이 \(oldValue)원에서 \(credit)원으로 변경되었습니다")
}
}
var dollarValue: Double {
get {
return Double(credit)/1000
}
set {
credit = Int(newValue * 1000)
// [4]
print("잔액이 \(newValue)달러로 변경 중입니다")
}
}
}
class ForeignAccount: Account { // Account를 상속받음
override var dollarValue: Double {
willSet {
// [1]
print("잔액이 \(dollarValue)달러에서 \(newValue)달러로 변경될 예정입니다")
}
didSet {
// [5]
print("잔액이 \(oldValue)달러에서 \(dollarValue)달러로 변경되었습니다")
}
}
}
let myAccount: ForeignAccount = ForeignAccount()
// 잔액이 0원에서 1000원으로 변경될 예정입니다.
myAccount.credit = 1000 // 잔액이 0원에서 1000원으로 변경되었습니다
// [1] 잔액이 1.0달러에서 2.0달러로 변경될 예정입니다
// [2] 잔액이 1000원에서 2000원으로 변경될 예정입니다.
// [3] 잔액이 1000원에서 2000원으로 변경되었습니다
myAccount.dollarValue = 2 // [4] 잔액이 2.0달러로 변경 중입니다
// [5] 잔액이 1.0달러에서 2.0달러로 변경되었습니다
5. 타입 프로퍼티
- 타입 자체에 속하는 프로퍼티
- 해당 타입의 모든 인스턴스가 공통으로 사용 가능
- 모든 인스턴스에서 공용으로 접근하고 값을 변경할 수 있음 (var로 선언된 경우)
- 저장 타입 프로퍼티인 경우 초깃값 반드시 설정해야 함
- 인스턴스 생성 없이도 사용 가능
class AClass {
// 상수 역할
static let typeConstantProperty: Int = 100
// 저장 타입 프로퍼티
static var typeProperty: Int = 0
// 저장 인스턴스 프로퍼티
var instanceProperty: Int = 0 {
didSet {
// AClass.typeProperty와 Self.typeProperty는 같은 말이다
Self.typeProperty = instanceProperty + 100
}
}
// 연산 타입 프로퍼티
static var typeComputedProperty: Int {
get {
return typeProperty
}
set {
typeProperty = newValue
}
}
}
AClass.typeProperty = 123
let classInstance: AClass = AClass()
print(AClass.typeProperty) // 123
classInstance.instanceProperty = 100
print(AClass.typeProperty) // 200
print(AClass.typeComputedProperty) // 200
6. 키 경로
- 값을 바로 꺼내오는 것이 아니라 어떤 프로퍼티의 위치만 참조
- 어떤 프로퍼티 값을 가져야 할 지 미리 지정하여 사용 가능
- 키 경로 타입은 AnyKeyPath 라는 클래스로부터 파생됨
- WritableKeyPath <Root, Value>은 값 타입의 키 경로타입 / 읽고 쓸 수 있는 경우 적용
- ReferenceWritableKeyPath <Root, Value>은 참조 타입의 키 경로 타입 / 읽고 쓸 수 있는 경우 적용
- KeyPath은 읽을 수만 있는 경우 적용
class Person {
var name: String
init(name: String) {
self.name = name
}
}
struct Stuff {
var name: String
var owner: Person
}
print(type(of: \Person.name)) // ReferenceWritableKeyPath<Person, String>
print(type(of: \Stuff.name)) // WritableKeyPath<Stuff, String>
// 키 경로 타입의 경로 연결
let keyPath = \Stuff.owner
let namePath = keyPath.appending(path: \.name)
class Person {
var name: String
init(name: String) {
self.name = name
}
}
struct Stuff {
var name: String
var owner: Person
}
let ttukttak = Person(name: "ttukttak")
let jin = Person(name: "jin")
let macbook = Stuff(name: "MacBook Pro", owner: ttukttak)
var iMac = Stuff(name: "iMac", owner: ttukttak)
let iPhone = Stuff(name: "iPhone", owner: jin)
let stuffNameKeyPath = \Stuff.name
let ownerKeyPath = \Stuff.owner
// \Stuff.owner.name과 같은 표현
let ownerNameKeyPath = ownerKeyPath.appending(path: \.name)
// 키 경로와 서브스크립트를 이용해 프로퍼티에 접근, 값 가져옴
print(macbook[keyPath: stuffNameKeyPath]) // MacBook Pro
print(iMac[keyPath: stuffNameKeyPath]) // iMac
print(iPhone[keyPath: stuffNameKeyPath]) // iPhone
print(macbook[keyPath: ownerNameKeyPath]) // ttukttak
print(iMac[keyPath: ownerNameKeyPath]) // ttukttak
print(iPhone[keyPath: ownerNameKeyPath]) // jin
// 키 경로와 서브스크립트를 이용해 프로퍼티에 접근, 값 변경
iMac[keyPath: stuffNameKeyPath] = "iMac Pro"
iMac[keyPath: ownerKeyPath] = jin
'swift' 카테고리의 다른 글
[swift] 옵셔널 (0) | 2020.04.28 |
---|---|
[swift] 메소드 (method) (0) | 2020.04.27 |
[swift] 구조체(struct)와 클래스(class) 비교 (0) | 2020.04.08 |
[swift] 연산자 (0) | 2020.04.06 |
[swift] 데이터 타입 (0) | 2020.04.02 |