iOS 스터디 Part5 Properties
Properties.
Swift에서는 값을 저장하는 Stored Properties와 함수와 유사(저장 공간을 제공하지 않고 getter/setter 하는 방법을 제공)한 Computed Properties로 구분
Properties는 변수처럼 생각하면 쉽다.
예를 들어, 아래의 record라는 배열은 Stored Properties이다.
import CoreLocation
struct GPSTrack {
var record: [(CLLocation, Date)] = []
}
** 만약, 외부에서는 read-only 상태로 바꾸려면 private(set)
또는 fileprivate(set)
키워드를 사용할 수 있다.
get, set을 이용한 Computed Properties를 만들 수 있다.
extension GPSTrack {
var totalDistance: CLLocationDistance {
get {
var distance: CLLocationDistance = 0.0
// write getter logic
return distance
}
set(newDistance) {
// write setter logic
}
}
}
Change Observers.
Stored Properties와 변수에 대한 willSet
및 didSet
핸들러를 구현할 수 있습니다.
이들은 속성가 설정될 때마다 호출(값이 변경되지 않아도)
willSet
: 값이 저장되기직전에 호출됩니다.
didSet
: 새로운 값이 저장된직후에 호출됩니다.
Property가 선언된 위치에서 정의되어야 합니다. 따라서 extension에서 Property Observer를 나중에 추가할 수는 없습니다.
willSet, didSet에 특정 값을 넘겨주지 않으면 각각 newValue, oldValue로 값을 받을 수 있다.
class StepCounter {
var totalSteps: Int = 0 {
willSet(newState) {
print("totalSteps: \\(newState)")
}
didSet(oldState) {
print("\\(totalSteps - oldState) step")
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// willSet이 동작 새로운 값 newValue = 200 (출력:totalSteps: 200)
// didSet이 동작 기존 값 oldValue = 0 (출력: (200 - 0) step)
서브클래스에서 관찰자를 추가하려면 속성을 오버라이드할 수 있습니다. 다음은 Robot
클래스의 state
속성을 오버라이드하여 willSet
Observer를 추가하는 예제입니다.
class Robot {
enum State {
case stopped, movingForward, turningRight, turningLeft
}
var state = State.stopped
}
class ObservableRobot: Robot {
override var state: State {
willSet(newState) {
print("Transitioning from \\(state) to \\(newState)")
}
}
}
var robot = ObservableRobot()
robot.state = .movingForward // 출력: Transitioning from stopped to movingForward
Swift에서의 Property Observer는 순수하게 컴파일 타임 특성입니다.
Lazy Stored Properties.
값을 게으르게(lazily) 초기화하는 것은 Swift에서 흔한 패턴 중 하나
사용할 때 초기화하는 패턴입니다. → 메모리를 효율적으로 관리할 수 있기 때문
lazy 속성은 초기화가 완료된 후에야 초기 값이 설정될 수 있으므로 항상 var(값을 변경할 수 있는)로 선언되어야 합니다.
lazy는 Stored Properties에만 사용 가능합니다. 최초 값을 올린 값을 사용
struct, class에만 사용 가능하다(enum에는 불가능)
예를 들어, GPSTrack을 표시하는 뷰 컨트롤러가 있다고 가정해봅시다.
해당 트랙의 미리보기 이미지를 가지려면 해당 속성을 lazy로 만들어 첫 번째로 속성에 액세스할 때까지 비용이 많이 드는 이미지 생성을 지연시킬 수 있습니다.
class GPSTrackViewController: UIViewController {
var track: GPSTrack = GPSTrack()
lazy var preview: UIImage = {
for point in track.record {
// 어떤 비용이 많이 드는 계산 수행.
}
return UIImage(/* ... */)
}()
}
속성이 처음 액세스될 때, 클로저가 실행되고(괄호에 주목), 그 반환 값이 속성에 저장하는 패턴을 사용하여 다른 Property를 클로저 내부에서 접근하는 방식을 취한다.
distanceFromOrigin를 lazy 계산된 속성으로 저장합니다.
struct Point {
var x: Double
var y: Double
private(set) lazy var distanceFromOrigin: Double = (x*x + y*y).squareRoot()
init(x: Double, y: Double) {
self.x = x
self.y = y
}
}
Point를 생성하면 distanceFromOrigin 속성에 액세스하여 값이 계산되고 재사용을 위해 저장됩니다. 그러나 x 값을 변경하면 distanceFromOrigin에는 반영되지 않습니다.
var point = Point(x: 3, y: 4)
point.distanceFromOrigin // 5.0
point.x += 10
point.distanceFromOrigin // 5.0
lazy 속성에 액세스하는 것은 속성의 초기 값이 첫 액세스시에 설정되기 때문에 변이(mutation) 작업입니다.
Point 인스턴스를 참조하는 변수의 값이 변경되는 경우에는 var를 써야한다.(아래의 경우 var)
let immutablePoint = Point(x: 3, y: 4)
immutablePoint.distanceFromOrigin
Question.
Lazy keyword를 enum에는 사용하지 못하는 이유?
Property Observer가 컴파일 타임 특성인 이유?
Last updated