프로퍼티 (Properties)
✅ 프로퍼티는 클래스, 구조체, 열거형과 관련한 값이다.
저장 프로퍼티 (Stored Properties) | 값을 저장하고 있는 프로퍼티 ⚠️ 클래스와 구조체에서만 사용 가능하다. |
계산된 프로퍼티(Computed Properties) | 값을 저장하고 있지 않고 특정하게 계산한 값을 반환해 주는 프로퍼티 ⚠️ 클래스, 구조체, 열거형 모두 사용 가능하다. |
저장 프로퍼티 (Stored Properties)
struct Eplteam {
var team1: String
var team2: String
}
let eplTeam1 = Eplteam(team1: "LiverPool", team2: "Chelsea")
✔️ 위의 예제와 같이 let, var 키워드를 이용해서 변수로 선언해 사용할 수 있다.
상수 구조체 인스턴스의 저장 프로퍼티 (Stored Properties of Constant Structure Instances)
struct Eplteam {
var team1: String
var team2: String
}
let eplTeam1 = Eplteam(team1: "LiverPool", team2: "Chelsea")
eplTeam1.team2 = "ManCity" // ERR.
struct Eplteam {
var team1: String
let team2: String
}
var eplTeam1 = Eplteam(team1: "LiverPool", team2: "Chelsea")
eplTeam1.team2 = "ManCity" // ERR.
✔️ 위의 예제와 같이 구조체를 상수로 선언하면 그 구조체 인스턴스의 프로퍼티를 변경할 수 없다.
지연 저장 프로퍼티 (Lazy Stored Properties)
지연 저장 프로퍼티는 값이 처음으로 사용 되기 전에는 계산되지 않는 프로퍼티이다. 지연 저장 프로퍼티는 프로퍼티가 특정 요소에 의존적이어서 그 요소가 끝나기 전에 적절한 값을 알지 못하는 경우에 유용하다. 또 복잡한 계산이나 부하가 많이 걸리는 작업을 지연 프로퍼티로 선언해 사용하면 실제 사용되기 전에는 실행되지 않아서 인스턴스의 초기화 시점에 복잡한 계산을 피할 수 있다.
✅ 지연 저장 프로퍼티로 선언하기 위해서는 프로퍼티의 선언 앞에 lazy 키워드를 붙이면 된다.
⚠️ 지연 저장 프로퍼티는 반드시 변수(var)로 선언해야 한다.
class DataImporter {
/*
DataImporter는 외부 파일에서 데이터를 가져오는 클래스입니다.
이 클래스는 초기화 하는데 매우 많은 시간이 소요된다고 가정하겠습니다.
*/
var filename = "data.txt"
// 데이터를 가져오는 기능의 구현이 이 부분에 구현돼 있다고 가정
}
class DataManager {
lazy var importer = DataImporter()
var data = [String]()
// 데이터를 관리하는 기능이 이 부분에 구현돼 있다고 가정
}
let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// DataImporter 인스턴스는 이 시점에 생성돼 있지 않습니다.
✔️ DataManager라는 클래스를 선언하고 이 클래스는 데이터를 가져오는 DataImporter클래스를 갖고 있다. 그리고 이 DataImporter는 실제 디스크 파일에서 데이터를 가져오기 때문에 초기화시 많은 시간이 소요된다. 그래서 이 클래스를 지연 프로퍼티(lazy var importer = DataImporter()) 로 선언한다. 이 프로퍼티는 코드에서 볼 수 있듯 DataManager 인스턴스 manager를 생성하고 거기에 data 를 넣어도 그 시점에 DataImporter인스턴스는 생성돼 있지 않다. 다시 말하면 지연 프로퍼티로 선언해 놓았기 때문에 실제 그 프로퍼티를 사용하기 전에는 복잡하고 시간일 오래 소요되는 연산을 할 필요가 없다는 것이다.
계산된 프로퍼티 (Computed Properties)
계산된 프로퍼티는 실제 값을 저장하고 있는 것이 아니라 getter와 optional한 setter를 제공해 값을 탐색하고 간접적으로 다른 프로퍼티 값을 설정할 수 있는 방법을 제공한다.
✅ get : 계산된 프로퍼티가 다른 프로퍼티의 값을 계산하여 간접적으로 값을 제공할 때 프로퍼티의 값을 참고하기 위해서 사용하는 구문이다. 함수와 비슷해서 return을 이용해서 반환하는데, 여기서 반환되는 값이 프로퍼티가 제공하는 값이 된다.
✅ set : 계산된 프로퍼티에 값을 할당하거나 변경하고자 할 때 실행되는 구문이다. 계산된 프로퍼티는 값 자체를 저장하지 않기 때문에 이때 할당되는 값은 연산의 중요한 요소이다.
⚠️ set 구문이 없으면 외부에서 계산된 프로퍼티에 할당할 수 없으며, 내부적인 연산 처리를 통해 값을 제공받는 읽기전용 프로퍼티가 만들어진다. 하지만 get 구문이 없으면 계산된 프로퍼티가 값이 반환하는 기능을 갖지 못하기 때문에 get 구문은 필수 요소이다.
class getsetEx {
var property: type {
get {
// 필요한 계산
return 반환값
}
set (param) {
// 필요한 연산
}
}
}
class Twice {
var x: Int = 10
var twiceX: Int {
get {
return x*2
}
set(twiceTarget) {
x = twiceTarget*2
}
}
}
var t = Twice()
t.twiceX // 계산된 프로퍼티인 twiceX의 값을 읽음
print(t.twiceX) // 20
t.twiceX = 100 // 계산된 프로퍼티인 twiceX에 값을 씀
print(t.x) // 200
✔️ 위의 예제처럼 원하는 연산을 해서 get을 작성할 수도 있고, 인자로 받은 값을 원하는 연산을 해서 set을 작성할 수 있다. 계산된 프로퍼티는 저장 프로퍼티처럼 사용하면 된다. 위에서는 계산된 프로퍼티의 값인 x의 값을 읽으면, twiceX의 get가 실행되어 20이라는 값이 나온 것이고, 계산된 프로퍼티의 x에 값을 쓰면, 100이라는 값이 set의 인자로 넘어가 set함수가 실행되어 200이라는 값이 나온다.
Setter 선언의 간략한 표현 (Shorthand Setter Declaration)
class Twice {
var x: Int = 10
var twiceX: Int {
get {
return x*2
}
set {
x = newValue*2
}
}
}
var t = Twice()
t.twiceX // 계산된 프로퍼티인 twiceX의 값을 읽음
print(t.twiceX) // 20
t.twiceX = 100 // 계산된 프로퍼티인 twiceX에 값을 씀
print(t.x) // 200
✔️ 위의 예제와 같이 set 인자 부분을 다 생략하고 접근을 newValue라는 이름으로 접근하면 된다.
⚠️ newvalue, Newvalue, NewValue 다 안된다. 오로지 newValue만 된다.
읽기전용 계산된 프로퍼티 (Read-Only Computed Properties)
class Twice {
var x: Int = 10
var twiceX: Int {
get {
return x*2
}
}
}
var t = Twice()
t.twiceX // 계산된 프로퍼티인 twiceX의 값을 읽음
print(t.twiceX) // 20
✔️ 위의 예제와 같이 set가 필요 없으면 get만 선언해 줄 수 있다.
class Twice {
var x: Int = 10
var twiceX: Int {
return x*2
}
}
var t = Twice()
t.twiceX // 계산된 프로퍼티인 twiceX의 값을 읽음
print(t.twiceX) // 20
✔️ 위의 예제와 같이 읽기전용으로 작성할 때는 get 구문 자체를 생략해서 더 간단하게 쓸 수 있다.
프로퍼티 옵저버 (Property Observers)
프로퍼티에는 새 값이 설정(set) 될 때마다 이 이벤트를 감지할 수 있는 옵저버를 제공한다. 이 옵저버를 프로퍼티 옵저버라 하는데 프로퍼티 옵저버는 새 값이 이전 값과 같더라도 항상 호출 된다. 즉, 프로퍼티 값의 변화를 관찰하는 것이다.
⚠️ 프로퍼티 옵저버는 지연 저장 프로퍼티에서는 사용할 수 없다.
⚠️ 계산된 프로퍼티는 set에서 값의 변화를 감지 할 수 있기 때문에 따로 옵저버를 정의할 필요가 없다.
willSet | 값이 저장되기 직전에 호출된다. |
didSet | 새 값이 저장된 직후에 호출된다. |
1️⃣ willSet
값이 저장되기 직전에 새로 저장될 값이 인자로 전달 된다. 이때 인자 이름은 지정할 수 있지만, 인자 이름과 괄호를 따로 지정하지 않을 경우 newValue로 사용한다.
var team: String = "FA" {
willSet(newTeam) {
print("현재 팀 = \(team), 이적 후 팀 = \(newTeam)")
}
}
✔️ 위의 예제와 같이 willSet 옵저버를 구현할 수 있다.
var team: String = "FA" {
willSet {
print("현재 팀 = \(team), 이적 후 팀 = \(newValue)")
}
}
✔️ 위의 예제와 같이 willSet 옵저버도 인자 부분을 생략하고 newValue라는 인자로 접근할 수 있다.
class Person{
var team: String = "FA" {
willSet {
print("현재 팀 = \(team), 이적 후 팀 = \(newValue)")
}
}
}
var p = Person()
p.team = "Liverpool"
// 현재 팀 = FA, 바뀔 팀 = Liverpool
✔️ 위의 예제와 같이 willSet을 호출하면 team이라는 프로퍼티의 값이 Liverpool로 변경되기 직전에 호출 되는 것이기 때문에 아직 team의 값은 Liverpool로 변경되기 전인 FA이다. 그래서 출력 값이 현재 팀 = FA, 바뀔 팀 = Liverpool로 출력된다.
2️⃣ didSet
값이 저장된 직후에 이전 프로퍼티의 값이 인자로 전달 된다. 이때 파라미터 이름은 지정할 수 있지만, 파라미터 이름과 괄호를 따로 지정하지 않을 경우 oldValue로 사용한다.
var team: String = "Liverpool" {
didSet(oldTeam) {
print("현재 팀 = \(team), 이적 전 팀 = \(oldTeam)")
}
}
✔️ 위의 예제와 같이 didSet 옵저버를 구현할 수 있다.
var team: String = "Liverpool" {
didSet {
print("현재 팀 = \(team), 이적 전 팀 = \(oldValue)")
}
}
✔️ 위의 예제와 같이 didSet 옵저버도 인자 부분을 생략하고 oldValue라는 인자로 접근할 수 있다.
class Person{
var team: String = "Liverpool" {
didSet {
print("현재 팀 = \(team), 이적 전 팀 = \(oldValue)")
}
}
}
var p = Person()
p.team = "FA"
✔️ 위의 예제와 같이 didSet을 호출하면 team이라는 프로퍼티의 값이 FA로 변경된 직후에 호출 되는 것이므로 저장 프로퍼티인 team의 값은 FA로 변경되어 현재 팀 = FA, 이적 전 팀 = Liverpool로 출력 된다.
class Person{
var team: String = "FA" {
willSet {
print("현재 팀 = \(team), 이적 후 팀 = \(newValue)")
}
didSet {
print("현재 팀 = \(team), 이적 전 팀 = \(oldValue)")
}
}
}
var p = Person()
p.team = "Liverpool"
✔️ 위의 예제처럼 willSet과 didSet을 같이 구현할 수 있다. team 프로퍼티 값을 변경하면 willSet이 먼저 실행된 다음 저장 프로퍼티 team의 값이 변경되고 didSet이 실행되어 현재 팀 = FA, 이적 후 팀 = Liverpool 현재 팀 = Liverpool, 이적 전 팀 = FA이 출력된다.
전역변수와 지역변수 (Global and Local Variables)
계산된 프로퍼티와 프로퍼티 옵저버 기능은 전역변수와 지역변수 모두에서 이용 가능하다. 전역 변수란 함수, 메소드, 클로저 혹은 타입 컨텍스트 밖에 정의된 변수이고 지역 변수는 그 안에 선언된 변수를 말한다.
타입 프로퍼티 (Type Properties)
인스턴스 프로퍼티는 특정 인스턴스에 속한 프로퍼티를 말한다. 이 프로퍼티는 새로운 인스턴스가 생성될 때마다 새로운 프로퍼티도 같이 생성된다. 타입 프로퍼티는 특정 타입에 속한 프로퍼티로 그 타입에 해당하는 단 하나의 프로퍼티만 생성된다.
✔️ 타입 프로퍼티에는 저장 타입 프로퍼티와 계산된 타입 프로퍼티가 존재한다.
✔️ 저장 타입 프로퍼티의 경우 선언할 당시 원하는 값으로 항상 초기화가 되어 있어야 한다.
✔️ 타입 프로퍼티는 특정 타입의 모든 인스턴스에 공통으로 사용되는 값을 정의할때 유용하다.
class Dog {
static var name: String = "Hodu"
static var info: String {
return name + "의 견종은 포메라니안이다."
}
}
✔️ 위의 예제와 같이 저장 프로퍼티와 계산된 프로퍼티 앞에다가 static을 붙이면 저장 타입 프로퍼티와 계산된 타입 프로퍼티가 된다.
class Dog {
static var name: String = "Hodu"
static var info: String {
return name + "의 견종은 포메라니안이다."
}
}
var d = Dog()
Dog.name // Hodu
Dog.info // Hodu의 견종은 포메라니안이다.
Dog.name = "Bam"
Dog.info // Bam의 견종은 포메라니안이다.
✔️ 위의 예제와 같이 타입 프로퍼티의 경우 타입 자체의 이름에 점문법을 통해 접근한다. Dog() 클래스의 인스턴스 d로는 접근이 불가능하다.
class Dog {
static let name: String = "Hodu"
static var info: String {
return name + "의 견종은 포메라니안이다."
}
}
var d = Dog()
Dog.name // Hodu
Dog.info // Hodu의 견종은 포메라니안이다.
Dog.name = "seongil" // ERR. 상수 변경 불가
✔️ 위의 예제와 같이 상수를 변경하려고 하면 에러가 발생한다.
'Swift > Swift 기본기' 카테고리의 다른 글
11. 생성자 (Initialization) - 1 (0) | 2023.08.01 |
---|---|
10. 제네릭 (Generic) (0) | 2023.07.31 |
08. 클래스과 구조체 (Classes and Structures) (0) | 2023.03.18 |
07. 열거형 (Enumerations) (0) | 2023.03.18 |
06. 클로저 (Closures) (0) | 2023.03.18 |