Stored Properies (저장속성)
가장 단순한 형식의 저장속성은 클래스나 구조체 인스턴스의 일부분인 상수, 변수이다.
저장속성은 var를 사용하면 변수, let을 사용하면 상수 저장속성이다.
저장속성(변수, 상수)은 기본값(default value)를 가질 수 있다.
struct FixedLengthRange {
var firstValue: Int
let length: Int
}
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
// the range represents integer values 0, 1, and 2
rangeOfThreeItems.firstValue = 6
// the range now represents integer values 6, 7, and 8
firstValue는 변수 저장속성이며 length는 상수 저장속성이다. length는 상수 속성이기 때문에 초기화 후에는 변경할 수 없다.
Stored Properties of Constant Structure Instances(상수 구조체 인스턴스의 저장 속성)
구조체 인스턴스를 생성하고 그 인스턴스를 상수에 할당하면, 변수 속성으로 선언한 경우에도 인스턴스의 속성을 수정할 수 없다.
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// this range represents integer values 0, 1, 2, and 3
rangeOfFourItems.firstValue = 6
// this will report an error, even though firstValue is a variable property
rangeOfFourItems는 상수로 선언했기 때문에 firstValue가 변수라도 값을 바꾸는게 불가능하다.
이는 구조체가 값타입(value type)이기 때문이다.
값 타입의 인스턴스를 상수 표시한다면 모든 속성도 상수가 된다.
Lazy Stored Properties (지연 저장 속성)
lazy stored property는 최초 접근 전 까지 초기값을 할당하지 않는 속성이다.
선언 앞에 lazy를 붙인다.
lazy속성은 항상 변수로 선언해야 한다. 값이 없다가 생기는 거기 때문에 이는 어찌보면 값의 변경이 이루어진다고 볼 수 있다.
let으로 선언할 수 없고 항상 var로 선언해야한다.
lazy속성은 속성의 초기값이 외부의 요인에 의존해서 인스턴스 초기화가 완료되기 전 까지 값을 알 수 없을 때 유용하다.
초기값이 복잡하거나 소요되는 자원이 커서 필요한게 아니라면 접근 전까지 수행하지 않는게 좋을 때 유용한다.
아래 예제는 lazy stored property를 사용하여 불필요한 복잡한 클래스의 초기화를 피한다.
class DataImporter {
/*
DataImporter 클래스는 외부 파일에서 데이터를 가져 오는 데 사용됩니다.
이 클래스는 초기화 하는 데 상당한 시간이 소요될 것으로 예상됩니다.
*/
var filename = "data.txt"
// DataImporter 클래스는 여기에서 데이터 가져 오기 기능을 제공합니다.
}
class DataManager {
lazy var importer = DataImporter()
var data: [String] = []
// DataManager 클래스는 여기에서 데이터 관리 기능을 제공합니다.
}
let manager = DataManager()
manager.data.append("일부 데이터")
manager.data.append("더 많은 데이터")
// importer 속성에 대한 DataImporter 인스턴스가 아직 생성되지 않았습니다.
DataImporter는 외부 파일에서 자료를 불러오는 클래스이다. 데이터를 불러오는데는 긴 시간이 걸린다.
DataManager클래스는 String배열에 접근하고 관리하는 역할이다.. 기능을 모두 보여주진 않은것.
DataManager는 외부 파일을 불러오지 않을 수도 있기 때문에 DataImporter인스턴스는 최초로 사용할 때 생성하는 것이 좋다.
이럴 때 lazy를 사용한다.
print(manager.importer.filename)
// importer 속성에 대한 DataImporter 인스턴스가 이제 생성되었습니다.
// "data.txt"를 출력합니다.
lazy로 표시한 속성이 초기화 되기 전에 동시에 여러개의 쓰레드가 접근할 경우 속성이 한 번만 초기화 될 거라는 보증은 없다.
Computed Properties (계산 속성)
클래스와 구조체, 열거형은 실제로 값을 저장하지 않는 계산속성도 정의할 수 있다.
간접적으로 다른 속성의 값을 가져오고 설정하는 getter와 setter를 제공한다.
// 두 차원 좌표계에서 한 점을 나타내는 구조체를 정의합니다.
struct Point {
var x = 0.0, y = 0.0
}
// 두 차원 영역의 크기를 나타내는 구조체를 정의합니다.
struct Size {
var width = 0.0, height = 0.0
}
// 원점을 포함하는 직사각형을 나타내는 구조체를 정의합니다.
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set(newCenter) {
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
}
// 원점이 (0.0, 0.0)이고 너비와 높이가 각각 10.0인 정사각형을 만듭니다.
var square = Rect(origin: Point(x: 0.0, y: 0.0),
size: Size(width: 10.0, height: 10.0))
// 정사각형의 중심점을 initialSquareCenter 상수에 할당합니다.
let initialSquareCenter = square.center
// initialSquareCenter는 (5.0, 5.0)에 위치합니다.
// 정사각형의 중심점을 (15.0, 15.0)으로 이동시킵니다.
square.center = Point(x: 15.0, y: 15.0)
// 정사각형의 원점이 이동한 위치를 출력합니다.
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// 결과: "square.origin is now at (10.0, 10.0)"
Rect 구조체는 center라는 계산 속성을 제공한다. Rect의 중심은 origin과 size로 항상 결정할 수 있으므로 Point값으로 저장할 필요가 없다. 그 대신 Rect는 center라는 계산 변수를 위한 사용자 정의 획득자?(읽기 계산속성) 및 설정자를 정의하여 직사각형의 center가 마치 실제 저장속성인 것처럼 작업한다. 하지만 실제 center속성은 메모리에 저장되지 않는 계산속성이다.
Shorthand Setter Declaration (짧게 줄인 설정자(setter) 선언)
새 값의 이름을 setter가 정의하지 않으면 newValue라는 기본 이름을 사용한다.
이 축약 표기점의 이점을 활용한 Rect구조체이다.
struct AlternativeRect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}
}
}
Shorthand Getter Declaration(짧게 줄인 획득자(읽기) 선언)
getter가 단일 표현식이라면 그 표현식을 암시적으로 리턴한다.
이축약을 최대한 활용한 Rect 구조체이다.
struct CompactRect {
var origin = Point()
var size = Size()
var center: Point {
get {
Point(x: origin.x + (size.width / 2),
y: origin.y + (size.height / 2))
}
set {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}
}
}
getter에서 return을 생략하는건 함수에서 Return을 생략한는 것과 동일한 규칙이다.
Resd-Only Computed Properties (읽기 전용 계산 속성)
getter는 있지만 setter는 없는 계산속성은 읽기 전용 계산 속성(read- only computed property)라고 한다.
읽기 전용 계산 속성은 항상 값을 리턴하며 접근할 수는 있지만 다른 값을 설정할 수는 없다.
읽기 전용 계산 속성을 포함한 계산속성은 고정된 값이 아니기 때문에 반드시 var키워드를 써서 변수 속성으로 선언해야한다.
인스턴스 초기화의 일부분으로 한번 설정하고 나면 값으 바꾸지 않는 상수 속성에만 let키워드를 사용해야한다.
get키워드와 중괄호를 제거하여 읽기 전용 계산 속성의 선언을 단순화 할 수 있다.
Copy code
// 큐브이드를 나타내는 구조체를 정의합니다.
struct Cuboid {
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double {
return width * height * depth
}
}
// 너비가 4.0, 높이가 5.0, 깊이가 2.0인 큐브이드를 만듭니다.
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
// fourByFiveByTwo의 부피를 출력합니다.
print("fourByFiveByTwo의 부피는 \(fourByFiveByTwo.volume)입니다.")
// 결과: "fourByFiveByTwo의 부피는 40.0입니다."
Type Properties(타입 속성)
인스턴스 속성은 한 특정 타입의 인스턴스에 속하는 속성이다. 그 타입의 인스턴스를 새로 생성할 때 마다 다른 어떤 인스턴스와도 구분된 자신만의 속성 값 집합을 가지게 된다.
그 타입의 어떤 하나의 인스턴스가 아니라. 타입 자체에 속하는 속성을 정의할 수 있다. 그 타입의 인스턴스를 아무리 많이 만들어도 타입의 속성은 늘 하나만 존재한다. 이런 종류의 속성을 type properties(타입속성)이라고 한다.
타입 속성은 해당 타입의 모든 인스턴스가 사용할 수 있는 상수, 또는 모든 인스턴스에 값을 전역으로 저장하는 변수 속성 같이 한 특정 타입의 모든 인스턴스에 보편적인 값을 정의하기에 유용하다.
stored type properties는 변수 또는 상수일 수 있다.
computed type properties는 항상 변수로 선언해야 한다.
저장 속성과 달리 저장 타입 속성엔 기본 값을 반드시 설정해야한다. 이는 타입 그 자체를 초기화하는 이니셜라이저가 없기 때문이다.
저장 타입 속성은 lazily하게 초기화된다. 동시에 여러 쓰레드가 접근할 때에도 단 한번만 초기화 되는것을 보증할 수 없다.
lazy로 표시할 필요도 없다.
Type Property Syntax(타입 프로퍼티 문법?)
C와 Objective-C에선, 타입과 결합한 정적 상수와 변수를 전역 정적 변수로 정의한다 ??
하지만 Swift에서는 타입 속성을 타입 정의의 일부분인 타입의 바깥 중괄호 안에서 작성하며, 각각의 타입 속성 영역은 자신이 지원하는 타입 영역으로 명시된다.
타입속성은 static 키워드로 정의한다. 클래스 타입의 계산 타입 속성 이라면 class키워드로 대신하여 상위 클래스의 구현을 하위 클래스가 재정의 하도록 허용할 수 있다.
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 1
}
}
enum SomeEnumeration {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 6
}
}
class SomeClass {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 27
}
class var overrideableComputedTypeProperty: Int {
return 107
}
}
Querying and Setting Type Properties(타입 프로퍼티 조회와 설정?)
타입 속성은 인스턴스 속성과 동일하게 점 접근문법으로 조회하며 설정한다. 하지만 타입 속성은 그 타입의 인스턴스에서가 아니라 타입에서 조회하고 설정한다.
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)
// Prints "Another value."
print(SomeEnumeration.computedTypeProperty)
// Prints "6"
print(SomeClass.computedTypeProperty)
// Prints "27"
Property Observers(속성관찰자)
속성 관찰자는 속성 값이 바뀌는 걸 관찰하여 응답한다. 속성 관찰자는 속성 값을 설정할 때마다(새 값이 현재 속성 값과 같아도) 호출된다.
속성 관찰자를 추가할 수 있는건 아래와 같다.
- 자신이 정의한 stored properties
- 자신이 상속한 stored properties
- 자신이 상속한 computed properties
상속한 속성이면 하위 클래스에서 그 속성을 재정의(overriding)함으로써 속성 관찰자를 추가할 수 있다.
자신이 정의한 계산 속성이면 관찰자를 생성하는 대신에 속성의 setter로 값이 바귐을 관찰할 수 있다.
속성에 대해 다음 두 관찰자를 모두 정의할지 하나만 정의할 지 선택할 수 있다.
- willSet : 값을 저장하기 직전에 호출된다.
- didSet : 값을 저장한 바로 뒤에 호출된다.
willSet관찰자를 구현하면 새로운 속성 값을 상수 매개변수로 전달한다. willSet구현부에서 이 매개변수에 이름을 지정할 수 있다.
구현 부에서 매개변수 이름과 괄호를 작성하지 않으면 newValue라는 기본 매개변수 이름으로 매개변수를 사용할 수 있다.
didSet관찰자를 구현하면 예전 속성 값을 담은 상수 매개변수를 전달한다. 매개 변수에 이름을 붙이거나 oldValue라는 기본 매개 변수 이름을 사용할 수 있다. 자신의 didSet 관찰자 안에서 속성에 값을 할당하면 새로운 할당값이 그 전에 설정한 거을 대체한다.
상위 클래스 속성의 willSet, didSet 관찰자는 슈퍼 클래스 초기화가 호출된 후 하위 클래스 초기화에서 프로퍼티가 설정될 때 호출된다. 수퍼클래스가 초기화 호출 전에 클래스 자체 프로퍼티를 설정하는 동안에는 호출되지 않는다.
다음은 willSet과 didSet동작의 예제이다.
class StepCounter {
var totalSteps: Int = 0 {
// 새로운 값이 할당되기 전에 호출되는 프로퍼티 감시자
willSet(newTotalSteps) {
print("totalSteps를 \(newTotalSteps)로 변경할 예정입니다")
}
// 새로운 값이 할당된 후에 호출되는 프로퍼티 감시자
didSet {
// 현재 값과 이전 값의 차이가 양수인 경우에만 출력
if totalSteps > oldValue {
print("\(totalSteps - oldValue) 걸음 추가되었습니다")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// totalSteps를 200로 변경할 예정입니다
// 200 걸음 추가되었습니다
stepCounter.totalSteps = 360
// totalSteps를 360로 변경할 예정입니다
// 160 걸음 추가되었습니다
stepCounter.totalSteps = 896
// totalSteps를 896로 변경할 예정입니다
// 536 걸음 추가되었습니다
참고
https://developer.apple.com/documentation/swift