오늘은 Swift의 Property
에 알아보자.
Property
에 대해 정리하고자 한 이유는 코드리뷰에서 연산프로퍼티와 파라미터가 없는 메서드의 차이를 물어봐서도 있고, 싱글톤에 대해 포스팅하려하니 프로퍼티에 대해 알고 넘어가는게 좋을거 같아서 포스팅한다.
프로퍼티는 값을 특정
class
,struct
,Enum
과 연결한다.
Swift Property
는 3가지가 있다.
인스턴스의 변수, 상수이다.
class
, struct
에서만 사용할 수 있다.
Stored Property
처럼 값을 저장하는 것이 아니라 특정 연산을 실행한 결과값이다.
즉, Computed Property
는 값을 연산한다.
class
, struct
, Enum
에 사용할 수 있다.
특정 타입에 사용되는 Property
이다.
이 포스팅에서는 Stored Property
에 대해 알아보자.
class
또는struct
인스턴스와 연관된 값을 저장한다.
var
, let
을 사용할 수 있다.
var
로 선언하면 변수를 저장하는 것이고, let
으로 선언하면 상수를 저장하는 것이다.
저장 프로퍼티를 선언할 때 저장할 기본값을 지정할 수 있다.
struct FixedLengthRange {
var firstValue: Int
let length: Int
}
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
rangeOfThreeItems.firstValue = 6
(예제는 Swift.org에서 가져왔다.)
위의 예제에서 저장 프로퍼티는 firstValue
와 length
이다.
var
로 선언된 firstValue
는 변수 저장 프로퍼티이고, let
으로 선언된 length
는 상수 저장 프로퍼티이다.
var
로 rangeOfThreeItems
라는 FixedLengthRange
인스턴스를 만들었다.
인스턴스를 만들때 firstValue
에는 0, length
에는 3을 선언했다.
변수 저장 프로퍼티인 firstValue
값을 바꿔보자.
rangeOfThreeItems.firstValue = 6
을 통해 rangeOfThreeItems
(FixedLengthRange
의 인스턴스)의 firstValue
값이 바뀐 모습을 확인할 수 있다.
그렇다면 상수 저장 프로퍼티인 length
의 값을 바꿀 수 있을까?
❗️에러가 뜬다!
에러를 확인해보니 상수여서 값을 변경할 수 없다고 한다!
struct
는 저장 프로퍼티가Optional
이 아니여도 저장 프로퍼티를 모두 포함한 이니셜라이저를 자동으로 생성한다.
struct Person {
var name: String
var age: Int
}
let wonhee: Person = Person(name: "wonhee", age: 20)
struct
에 이니셜라이저를 따로 생성하지 않아도 자동으로 생성해준다.
class
의 저장 프로퍼티는Optional
이 아니면Property
기본값을 지정해주거나 사용자 정의 이니셜라이저를 통해 반드시 초기화해줘야 한다.
(상수 프로퍼티는 인스턴스가 초기화 될 때 한 번만 값이 할당 된다.)
(자식 클래스에서 초기화 변경이 불가하다.)
class Korean {
var citizen: Person
let cityName: String
}
let wonheeCitizen: Korean = Korean(citizen: Person(name: "wonhee", age: 20), cityName: "Anyang")
그렇다면 위의 struct
처럼 class
도 자동으로 이니셜라이저가 생성될까?
이니셜라이저가 없어 에러가 뜬다!
그렇다면 정상 코드로 바꿔보자.
class Korean {
var citizen: Person
let cityName: String
init(citizen: Person, cityName: String) {
self.citizen = citizen
self.cityName = cityName
}
}
let wonheeCitizen: Korean = Korean(citizen: Person(name: "wonhee", age: 20), cityName: "Anyang")
위의 코드처럼 이니셜라이저를 생성해주면 해결할 수 있다.
저장 프로퍼티에 기본값을 준다면 사용하기 편해지지 않을까?
한 번 확인해보자.
struct Person {
var name: String = "name"
var age: Int = 10
}
let some: Person = Person()
let wonhee: Person = Person(name: "wonhee", age: 20)
Person
구조체에 name
, age
저장 프로퍼티에 각각 "name", 10이라는 기본값을 주었다.
코드를 확인해보면 파라미터가 없는 이니셜라이저로 생성한 인스턴스는 저장 프로퍼티의 기본값이 들어가 있음을 확인할 수 있다.
class Korean {
var citizen: Person = Person()
var cityName: String = "city"
}
let wonheeCitizen = Korean()
Korean
class 저장 프로퍼티에도 기본값을 주었더니 이니셜라이저가 없어도 잘 동작함을 확인할 수 있다.
하지만 아래와 같은 코드가 없다면 wonheeCitizen
인스턴스의 cityName
저장 프로퍼티에는 "city"가 저장되어 있다.
let wonheeCitizen = Korean()
wonheeCitizen.cityName = "Anyang"
위와 같은 코드를 통해 cityName
저장 프로퍼티 값을 변경할 수 있다.
초기값을 미리 지정해두면 인스턴스 생성 과정이 편해진다.
하지만 초기값이 아닌 다른 값을 사용하고 싶을때에는 직접 프로퍼티를 할당해주는 과정이 불편하다.
이런 불편함은 원하지 않은 값을 사용하게 되는 에러의 원인이 될 수도 있다.
또한,cityName
을 상수로 사용하고 싶어져도 직접 프로퍼티를 할당해주는 과정을 위해let
으로 선언할 수 없다.
struct Person {
var name: String
var age: Int
}
class Korean {
var citizen: Person?
let cityName: String
init(cityName: String) {
self.cityName = cityName
}
}
let wonhee: Korean = Korean(cityName: "Anyang")
wonhee.citizen = Person(name: "wonhee", age: 20)
Lazy Stored Property
는 지연 저장 프로퍼티라고 부른다.
지연 저장 프로퍼티는 호출이 있어야 값을 초기화한다.
lazy
라는 키워드를 사용해서 선언한다.
상수는 인스턴스가 완전히 생성되기 전에 초기화해야 하므로 필요할때 값이 할당되는 지연 저장 프로퍼티에는 사용할 수 없다.
즉, 지연 저장 프로퍼티는 var
만 사용할 수 있다.
그렇다면 코드를 통해 이해해보자.
class 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")
DataManager
class에 importer
라는 지연 저장 프로퍼티가 있다.
manager
에 DataManager
인스턴스를 선언했다.
manager.data.append("Some data")
manager.data.append("Some more data")
에서는 지연 저장 프로퍼티 importer
를 호출하지 않아 importer
는 값이 초기화되지 않았다.
class 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")
print(manager.importer.filename)
마지막 줄의
print(manager.importer.filename)
에서 지연 저장 프로퍼티 importer
를 호출해서 이때 importer
의 값이 초기화되었다.
주로 복잡한 class
혹은 struct
에서 사용한다.
이런 점을 알고 지연 저장 프로퍼티를 사용한다면 성능을 높이고 공간 낭비를 줄일 수 있다.
위에서 지연 저장 프로퍼티에 대해 말할때에도 나왔지만 var
을 사용해서 선언해야한다.
멀티 thread 환경에서 여러 thread가 동시다발적으로 지연 저장 프로퍼티에 접근한다면 프로퍼티가 한 번만 초기화 된다는 것을 보장할 수 없다.
이전부터 프러퍼티에 대해 정리하려고 했는데 이제서야 했다.
정리하지 않아도 사용하는데 문제는 없었지만 그래도 한 번 정리하는 편이 나만의 기준을 세우는데 좋을거 같아서 정리해봤다.
정리하다보니 프로퍼티에 대한 내용이 꽤되서 시리즈처럼 나눠서 정리하려한다.
그럼 이만👋