momodudu.zip
#3 Swift - Properties 본문
Property란, 특정 class, enumeration, structure에 속하는 특정 value를 의미한다.
Swift에서는 Stored Property, Computed Property, Type Property가 있다.
자세하게 설명하기전에 간단하게 말하자면, stored property는 constant, variable 등 말 그대로 "저장"을 위해서 사용하는 value이다.
Computed property는 value를 저장하지 않고, 연산을 하는 getter 혹은 optional setter가 있다.
Type property는 "Type"그 자체와 연관된 Property인데, 말은 어렵지만 쉽게 설명하자면 c++ class의 static 변수라고 보면 된다.
즉 Instnace마다 정의되는게 아니라, class의 타입 통틀어서 하나만 존재하는 Property를 의미한다.
1. Stored Property
특정 class나 structure의 instance 내부에 저장되는 value를 의미한다. c++ class의 멤버변수 정도로 보면 되겠다.
struct RectStruct{
var width: Int = 10
var height: Int = 10
}
class RectClass{
var width: Int = 10
var height: Int = 10
}
위 예제에서 width, height 들을 stored property 라고 부른다.
Lazy Stored Property
class RectClass{
// Default value
var width: Int = 10
var height: Int = 10
// initializer
init(width: Int, height: Int){
self.width = width
self.height = height
}
}
class의 경우 , 기본적으로 위와 같이 initialize가 필요하다.
default value로 초기화를 하거나, initializer로 초기화를 해주지 않으면 컴파일 에러가 난다.
그런데 클래스의 몸집이 커지다보면 initialize 자체에 과부하가 걸릴 수 있다.
그래서 system이 처음 시작할때 initlaiize 단계에서 과부하가 걸리게 되면 시스템 전체적으로 느려질 수 있다.
그래서 init이 당장 필요하지 않은 변수들에 대해서는 초기화를 늦출 수 있는데, 그게 lazy stored property 이다.
class DataImporter{
var fileName = "data.txt"
// ....
}
class DataManager{
lazy var importer = DataImporter()
var data = [String]()
}
DataImport를 포함하고 있는 DataManager class 구조이다.
예를들어 Importer에서 file read같은 동작을 하게 된다면 initialize 단계에서 file read 와 같은 system call 이 발생할 수 있다.
그래서 importer의 경우, 실제로 사용하는 단계에서 초기화를 하도록 위와 같이 var 앞에 lazy keyword를 붙여주면
실제로 importer에 접근할 때 초기화를 시켜준다.
이처럼 lazy property로 선언하려면 반드시 var 로 선언해야 한다.
2. Computed Property
stored property에서는 실제로 어떤 값을 저장했다면, computed property는 값을 저장하는게 아니라
어떤 값을 가지고 연산하는 property를 의미한다. 흔히 얘기하는 getter/setter이다.
struct Point{
var x = 0.0
var y = 0.0
}
struct Size{
var width = 0.0
var 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{
origin.x = newValue.x - (size.width/2)
origin.y = newValue.y - (size.height/2)
}
}
}
위와 같이 정의된 클래스가 있다고 할 때, center에 엮이는 get/set이 computed property로 볼 수 있다.
center는 변수값이지만 center에 변수를 set 하거나 get 할때마다 이와 같은 computed property가 수행된다.
get만 쓸 수도 있지만 set은 optional이라 set만 쓸 수 없다. get과 같이 써야 한다.
get만 선언된 경우에는 Read-only computed property라고 부른다.
Property Observer
Swift에서는 특정 변수가 바뀔때마다 바뀌기 직전/직후로 특정 동작을 정의할 수 있는데, 그게 property observer다.
바뀌기 직전인 willSet과 바뀌고 난 직후인 didSet으로 정의할 수 있다.
struct Rect{
var origin = Point()
var size = Size()
var center: Point{
willSet(newCenter){
print("center will be changed \(center.x), \(center.y) -> \(newCenter.x), \(newCenter.y)")
}
didSet(oldCenter){
print("center was changed \(oldCenter.x), \(oldCenter.y) -> \(center.y),\(center.y)")
}
}
}
위처럼 willSet은 바뀌기 직전, 즉 아직 instance 내부의 center는 변하지 않은 상태고 곧 바뀔 값인 newCenter를 들고 있다.
didSet은 바뀐 직후, 즉 현재 instance에 assign이 이미 되어있는 상태고, 그 전의 값을 들고 있다.
그래서 이처럼 property를 계속해서 observe를 할 수 있다.
Property Wrapper
특정 property의 get/set을 마치 interface처럼 관리할 수 있다.
만약 어떤 숫자를 get/set할 건데, 그 숫자를 항상 특정 숫자 아래로만 관리를 하고 싶은 상황이라고 하면,
모든 property마다 get/set에다가 12를 넘지 않은 상태로 관리하도록 정의할 수 있다.
그렇지만 이런 Property수가 많아진다면 코드 중복이 많아질 것이다.
그래서 사용하는게 바로 이 Property wrapper다. property에 대한 interface정도로 정의하면 될 것 같다.
Swift예제에 따라 어떤 특정 숫자가 12를 넘지 못하도록 Property를 감싸는 wrapper를 작성할 수 있다.
@propertyWrapper
struct TwelveOrLess{
private var number:Int = 0
var wrappedValue: Int{
get
{
return number
}
set
{
number = min(number,12)
}
}
}
struct 선언과 유사하지만 앞에 @propertyWrapper keyword를 붙여주고
실제 property 동작이 어떻게 정의될지 구현을 위해 number라는 변수를 선언한다.
그리고 이 number의 wrapped value의 동작을 getter/setter로 정의해준다.
get은 그대로 리턴해주고, set의 경우 12를 넘지 않도록 정의한다.
그리고 이제 이 껍데기를 실제로 property에 씌워야 한다.
struct SmallRect{
@TwelveOrLess var height:Int
@TwelveOrLess var width:Int
}
height, width에 대해 위 wrapper를 씌우기 위해 @WrapperName을 같이 적어주면 된다.
이렇게 되면 실제로 SmallRect의 height, width에 어떤값을 넣더라도 12를 넘지 않는다.
이 wrapper에도 initializer를 정의할 수 있다.
3. Type Property
위에서 설명한 stored, computed property는 모두 class, structrue 혹은 enum의 instance자체에 종속되는 값들이었다.
즉, instance 하나마다 각 property가 할당되는 값이다.
type property의 경우, instance가 아닌 type 그 자체에 종속되는 값이다.
c++ class의 static variable과 유사하다고 보면 된다.
예를들어 Point class를 하나 정의했다고 치면,
class Point{
static var maxSize = 10
var size: Int
init(size: Int){
self.size = size
}
}
var pointList = [Point]()
var point1 = Point(size:1)
var point2 = Point(size:2)
Point Class에서 stored property는 위에서 설명한대로 size가 될 것이다.
그리고 이 Point class의 size property는 point1, point2 마다 할당된다.
그러나 point의 max size를 정의한 static var maxSize의 경우에는 point1, point2에 종속되지 않고
Point class자체에 종속된다. 그래서 이 값을 접근할때도 type 자체로 접근해야 한다.
Point.maxSize = 15
주의할점은 class의 initializer 는 instance 단위로 호출되므로, 이 type property의 초기화는 default value 값 설정으로만 할 수 있다.
그리고 이 type property도 위에서 언급한 stored, computed type 두가지가 모두 존재한다.
즉, stored instance property / computed instance property와,
stored type property / computed type property 이렇게 존재한다고 볼 수 있다.
'ios > Swift' 카테고리의 다른 글
#6 Swift - Extension (0) | 2019.11.28 |
---|---|
#5 Swift - Optional chaining (0) | 2019.11.27 |
#4 Swift - Initialization (0) | 2019.11.26 |
#2 Swift - Functions and Closures (0) | 2019.11.25 |
#1 Swift 기본 문법 - Basics : constant, variables, Optionals (0) | 2019.11.22 |