[Swift] 프로퍼티와 메서드
참조 : Swift 스위프트 프로그래밍 3판 Swift 5, 저자 야곰 (한빛미디어) , The Swift Programming Language Swift 5.6 Edition (iBooks)(https://books.apple.com/kr/book/the-swift-programming-language-swift-5-6/id881256329)
프로퍼티 (Property)
- 프로퍼티 : 클래스, 구조체 또는 열거형 등에 관련된 값
- 프로퍼티의 종류
- 저장 프로퍼티 : 인스턴스의 변수 또는 상수, 구조체와 클래스에서 사용가능
- 연산 프로퍼티 : 값을 저장한 것이 아니라 특정 연산을 실행한 결괏값, 클래스와 구조체, 열거형에서 사용 가능
- 타입 프로퍼티 : 특정 타입에 사용되는 프로퍼티
- 프로퍼티 감시자 : 프로퍼티의 값이 변화하는 것을 감시, 프로퍼티의 값이 변할 때 값의 변화에 따른 특정 작업을 실행,
저장 프로퍼티에 적용 가능, 부모 클래스로 부터 상속 가능
저장 프로퍼티
- 클래스 또는 구조체의 인스턴스와 연관된 값을 저장하는 가장 단순한 개념의 프로퍼티
- var 키워드를 사용하면 변수 저장 프로퍼티
- let 키워드를 사용하면 상수 저장 프로퍼티
- 구조체는 프로퍼티에 맞는 이니셜라이저를 자동으로 제공, 클래스에서는 그렇지 않아 클래스의 저장 프로퍼티에 초깃값을 지정하면 따로 사용자 정의 이니셜라이저를 구현해줄 필요 없음
- 저장 프로퍼티에 옵셔널이 초깃값이면 넣어주지 않아도 됨
※ 인스턴스를 생성할 때 이니셜라이저를 통해 초깃값을 보내야 하는 이유
- 프로퍼티가 옵셔널이 아닌 값으로 선언되어 있기 때문 → 인스턴스는 생성할 때 프로퍼티가 값이 있는 상태여야 함
// 저장프로퍼티에서 초깃값 지정
import UIKit
struct CoordinatePoint{
var x: Int = 0
var y: Int = 0
}
// 프로퍼티의 초깃값을 할당했다면 굳이 전달인자로 초깃값을 넘길 필요가 없음
let sonPoint: CoordinatePoint = CoordinatePoint()
let kimPoint: CoordinatePoint = CoordinatePoint(x: 10, y: 5)
지연 저장 프로퍼티(Lazy Stored Properties)
- 호출이 있어야 값을 초기화함 → 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 kimPosition: Position = Position(name: "kim")
print(kimPosition.point) // CoordinatePoint(x: 0, y: 0)
※ 다중 스레드 환경에서 지연 저장 프로퍼티에 동시다발적으로 접근할 때 한 번만 초기화된다는 보장이 없음, 생성되지 않은 지연 저장
프로퍼티에 많은 스레드가 비슷한 시점에 접근한다면 → 여러 번 초기화될 수 있음
연산 프로퍼티(Computed Properties)
- 특정 상태에 따른 값을 연산하는 프로퍼티
- 인스턴스 내/외부의 값을 연산하여 적절한 값을 돌려주는 접근자(getter)의 역할, 은닉화된 내부의 프로퍼티 값을 간접적으로
설정하는 설정자(setter) 역할도 가능
- 접근자인 get 메서드만 구현해둔 것처럼 읽기 전용 상태로 구현하기 쉽지만, 쓰기 전용 상태로 구현할 수 없다는 단점
struct CoordinatePoint{
var x: Int = 0
var y: Int = 0
var oppositionPoint: CoordinatePoint{
get{
return CoordinatePoint(x: -x, y: -y)
}
set(opposite) {
x = -opposite.x
y = -opposite.y
}
}
}
var kimPoint: CoordinatePoint = CoordinatePoint(x: 10, y: 20)
print(kimPoint) // CoordinatePoint(x: 10, y: 20)
kimPoint.oppositionPoint = CoordinatePoint(x: 15, y: 10)
print(kimPoint) // CoordinatePoint(x: -15, y: -10)
- 하나의 프로퍼티에 접근자와 설정자가 모두 모여있고, 해당 프로퍼티가 어떤 역할을 하는지 좀 더 명확하게 표현 가능
- 인스턴스를 사용하는 입장에서도 마치 저장 프로퍼티인 것처럼 편하게 사용 가능
- 설정자의 매개변수로 원하는 이름을 소괄호 안에 명시해주면 set메서드 내부에서 전달받은 인자 사용 가능
- newValue로 매개변수 이름을 대신할 수 있음, 이 경우에는 매개변수 따로 표기 X
- 접근자 내부의 코드가 단 한 줄이고, 그 결괏값의 타입이 프로퍼티의 타입과 같다면 return 키워드를 생략해도
그 결괏값이 접근자의 반환 값이 됨
- 읽기 전용으로 연산 프로퍼티를 구현하려면 get 메서드만 사용
프로퍼티 감시자(Property Observers)
- 프로퍼티의 값이 변경됨에 따라 적절한 작업을 취할 수 있음
- 프로퍼티의 값이 새로 할당될 때마다 호출
- 프로퍼티를 재정의해 상속받은 저장 프로퍼티 또는 연산 프로퍼티에도 적용 가능
※ 상속받지 않은 연산 프로퍼티에는 프로퍼티 감시자를 사용할 필요가 없으며 할 수도 없음
- willSet 메서드 : 프로퍼티의 값이 변경되기 직전에 호출하는 메서드
- didSet 메서드 : 프로퍼티의 값이 변경된 직후 호출하는 메서드
- willSet 메서드와 didSet 메서드에는 매개변수가 하나씩 보유, willSet 메서드에 전달되는 인자는 프로퍼티가 변경될 값,
didSet 메서드에 전달되는 전달인자는 프로퍼티가 변경되기 전의 값
- 매개변수의 이름을 따로 지정하지 않으면 willSet메서드에는 newValue, didSet메서드에는 oldValue로 자동 지정
- 매개변수의 이름을 따로 지정하고 싶으면 willSet이나 didSet 다음에 소괄호로 감싸고 이름 작성
class Account{
var credit: Int = 0{
willSet{
print("잔액이 \(credit)원에서 \(newValue)원으로 변경될 예정입니다.")
}
didSet{
print("잔액이 \(oldValue)원에서 \(credit)원으로 변경되었습니다.")
}
}
}
let myAccount: Account = Account()
myAccount.credit = 1000
// 잔액이 0원에서 1000원으로 변경될 예정입니다.
// 잔액 0원에서 1000원으로 변경되었습니다.
- 클래스를 상속받았다면 기존의 연산 프로퍼티를 재정의하여 프로퍼티 감시자를 구현할 수 있음
- 연산 프로퍼티를 재정의해도 기존의 연산 프로퍼티는 기능은 동작
※ 만약 프로퍼티 감시자가 있는 프로퍼티를 함수의 입출력 매개변수의 전달인자로 전달한다면 항상 willSet과 didSet 감시자 호출
전역변수와 지역변수
- 연산 프로퍼티와 프로퍼티 감시자는 전역변수와 지역변수 모두에 사용 가능
→ 프로퍼티에 한정하지 않고, 전역에서 쓸 수 있는 변수와 상수에도 두 기능 사용 가능
- 전역변수또는 지역변수는 저장변수라고 함
- 저장변수 : 저장 프로퍼티처럼 저장하는 역할
- 전역변수 또는 전역상수는 지연 저장 프로퍼티처럼 처음 접근할 때 최초로 연산이 이루어짐
- lazy 키워드를 사용하여 연산을 늦출 필요 X
- 지역변수 및 지역상수는 절대로 지연 연산되지 않음
var wonInPocket: Int = 2000{
willSet{
print("주머니의 돈이 \(wonInPocket)원에서 \(newValue)원으로 변경될 예정입니다.")
}
didSet{
print("주머니의 돈이\(oldValue)원에서 \(wonInPocket)원으로 변경되었습니다.")
}
}
var dollarInPocket: Double{
get{
return Double(wonInPocket)
}
set{
wonInPocket = Int(newValue * 1000.0)
print("주머니의 달러를 \(newValue)로 변경 중입니다.")
}
}
dollarInPocket = 3.5
// 주머니의 돈이 2000원에서 3500원으로 변경될 예정입니다.
// 주머니의 돈이 2000원에서 3500원으로 변경되었습니다.
// 주머니의 달러를 3.5달러로 변경 중입니다.
타입 프로퍼티
- 각각의 인스턴스가 아닌 타입 자체에 속하는 프로퍼티
- 타입 자체에 영향을 미침
- 인스턴스 생성 여부와 상관없이 프로퍼티의 값은 하나임
- 타입 프로퍼티의 두 가지 종류
- 저장 타입 프로퍼티 : 변수와 상수로 선언 가능
- 연산 타입 프로퍼티 : 변수로만 선언 가능, 반드시 초깃값 설정
- 다중 스레드 환경이라도 단 한 번만 초기화된다는 보장을 받음
- 지연 연산된다고 해서 lazy 키워드 사용할 필요 없음
- 타입 프로퍼티를 타입 상수로도 사용 가능
class Account{
static let dollarExchangeRate: Double = 1000.0
var credit: Int = 0
var dollarValue: Double{
get{
return Double(credit)
}
set{
credit = Int(newValue * Account.dollarExchangeRate)
print("잔액을 \(newValue)달러로 변경중입니다.")
}
}
}
키 경로
- 프로퍼티도 값을 바로 꺼내오는 것이 아니라 어떤 프로퍼티의 위치만 참조하도록 할 수 있음 → 키 경로(keyPath) 활용
- 키 경로를 사용하여 간접적으로 특정 타입의 어떤 프로퍼티 값을 가리켜야 할지 미리 정해두고 사용
- 키 경로의 구성
\타입이름.경로.경로.경로
- 키 경로를 잘 활용하면 타입 간의 의존도를 낮추는데 많은 도움을 줌
※ 키 경로는 타입 외부로 공개된 인스턴스 프로퍼티 혹은 서브 스크립트에 한해 표현 가능
- Swift 5.2 버전부터 (SomeType) -> Value 타입의 클로저를 키 경로 표현으로 대체 사용 가능
메서드
- 특정 타입에 관련된 함수
- 클래스, 구조체, 열거형 등은 실행하는 기능을 캡슐화한 인스턴스 메서드를 정의할 수 있음
- 기존의 프로그래밍 언어에서의 클래스 메서드와 유사
인스턴스 메서드
- 특정 타입의 인스턴스에 속한 함수
- 인스턴스의 내부의 프로퍼티 값을 변경하거나 특정 연산 결과를 반환하는 등 인스턴스와 관련된 기능을 실행
- 자신의 프로퍼티 값을 수정할 때 클래스의 인스턴스 메서드는 크게 신경 쓸 필요 없지만, 구조체나 열거형 등은 값 타입이므로
메서드 앞에 mutating 키워드를 붙여 해당 메서드가 인스턴스 내부의 값을 변경한다는 것을 명시해야 함
self 프로퍼티
- 모든 인스턴스는 암시적으로 생성된 self 프로퍼티를 가짐
- 자바의 this와 비슷한 개념
- 인스턴스를 더 명확히 지칭하고 싶을 때 사용
- 값 타입 인스턴스 자체의 값을 치환할 수 있음(구조체나 열거형일 경우)
callAsFunction
- 인스턴스를 함수처럼 호출할 수 있게 하는 키워드
- 매개변수와 반환타입만 다르다면 개수에 제한 없이 원하는 만큼 만들 수 있음
- mutating 키워드, throws와 rethrows도 함께 사용 가능
- 메서드를 호출하는 것 외에 함수 표현으로는 사용 불가
타입 메서드
- 타입 자체에 호출이 가능한 메서드
- 메서드 앞에 static 키워드를 사용하여 타입 메서드임을 나타내 줌
- 클래스의 타입 메서드는 static 키워드와 class 키워드로 사용 가능,
static으로 정의하면 상속 후 메서드 재정의가 불가, class로 정의하면 상속 후 메서드 재정의가 가능
- 인스턴스 메서드에서 self가 인스턴스를 가리킨다면, 타입 메서드의 self는 타입을 가리킴
'iOS > Swift' 카테고리의 다른 글
[Swift] 서브스크립트 (2) | 2022.09.22 |
---|---|
[Swift] print(_:separator:terminator:) (0) | 2022.09.21 |
[Swift] 구조체와 클래스 (0) | 2022.09.16 |
[Swift] 모나드 (0) | 2022.09.16 |
[Swift] 옵셔널(Optional) (0) | 2022.09.15 |