본문 바로가기

난 iOS개발자/iOS

Swift의 Property

728x90

저장 속성 Stored Property

- 가장 단순한 형태의 속성
- 일반적인 인스턴스의 상수 또는 변수
- 변수 저장은 var, 상수 저장은 let

init과 함께 인스턴스를 초기화 할 때 저장속성에 기본값을 제공할 수 있다.

struct FixedLengthRange {
  var firstValue: Int
  let length: Int
}

var rangeOfThreeItems = FixedLengthRange(firstValue: 0, lenth: 3)

rangeOfThreeItems.firstValue = 6


이 때 rangeOfThreeItems가 let으로 할당 된 인스턴스라면 FiexdLengthRange의 firstValue의 속성이 var(변수)여도 수정이 불가하다. 이것은 값 유형인 struct를 사용했기 때문이다. class를 사용한다면 내부 수정이 가능하다.


Lazy

지연 저장 속성은 첫 사용이 이뤄질 때 값이 계산된다.
인스턴스 초기화가 완료될 때 까지 초기 값이 할당되지 않을 수 있기 때문에 Lazy 속성은 var로 선언해야 한다.
상수 속성은 초기화가 완료되기 전에 항상 값을 가져야 하므로 Lazy으로 선언할수 없다.

Lazy는 사용되기 전까지 필요하지 않는 계산과 같은 설정을 사용할 때 유용하다.
사용전에 불필요한 메모리가 채워지는것을 막아준다.

class DataImporter {
  var fileName = "data.txt"
}

class DataManager {
  lazy var importer = DataImporter()
  var data: [String] = []
}

let manager = DataManager()
manager.data.append("Some Data")

위 코드에서 DataManager의 인스턴스 manager는 importer가 아직 생성되지 않았다.
lazy로 선언된 importer를 코드라인 어디에서도 접근하지 않았기 때문이다.

importer의 기능은 파일을 열고 그 내용을 메모리로 읽어야하는 기능이다. 이것은 상당한 비용과 시간이 걸릴것으로 추측 할 수 있고, 이러한 기능이 필요치 않은 상황인 경우 합리적인 인스턴스 생성을 위해 importer가 lazy로 선언되었다.

단점으로는

class Person {
  var name: String = "kSJ"
  lazy var nameLength: Int = {
    return name.count
  } 
}

let p = Person()
p.nameLength // 3
p.name = "Drake"
p.nameLength // 3


지연연산 프로퍼티 첫번째 호출시점에 초기화가 된 후 다른 값으로 갱신되지 못한다.

주의!

lazy가 아직 초기화 되지 않았을 경우 여러 스레드에서 동시 액세스가 발생한다면 속성이 한 번만 초기화 된다는 보장이 없다. ThreadSafe에 대해서 알아봐야 겠다.
이것을 ThreadSafe하게 하는 방법은 -> ? 그냥 해줬응면,,,



연산 속성 Computed Property


실제로 값을 저장하지 않고 계산된 속성을 정의 할 수 있다.
getter와 선택적 setter를 사용하여 연산속성을 제공할 수 있다.

struct Person {
  var name: String
  var myName: String {
    get {
      return name
    }
    set {
      name = newValue
    }
  }
}

var p = Person(name: "KSJ")
print(p.myName) // "KSJ"
p.myName = "new KSJ"
print(p.myName) // "new KSJ"


setter가 설정할 새 값의 이름을 정의하지 않으면 기본이름 newValue로 사용된다.
이때 get의 return은 생략 가능하다.

var name: String
var myName: String {
  get {
    name
  }
...


set은 옵셔널이기 때문에 작성하지 않아도 된다.
이 때 get만 남아있다면 읽기 전용 계산속성이 되므로
get을 생략한 아래 문법으로도 대치 가능하다.

struct Person {
  var name: String
  var myName: String {
    return name
  }
}



속성 관찰자 (Property Observers)

willSet, didSet으로
값이 변경 되려 할 때, 변경 후 를 관찰할 수 있다.

- willSet : 값이 저장되기 직전에 호출
- didSet : 새 값이 저장된 직후에 호출

class StepCounter {

  var totalSteps: Int = 0 {
    willSet(newTotalSteps) {
      print("About to sete totalSteps to\(newTotalSteps)")
    }
    
    didSet {
      if totalSteps > oldValue {
        print("Added \(totalSteps - oldValue) steps")
      }
    }
  }
}

let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps


마찬가지로 새로운 매개변수 이름을 제공하지 않는다면 oldValue와 newValue가 사용된다.
추가 -> **in-out과 will,did Set의 연관성에 대해 공부하자.**

728x90