콘텐츠로 건너뛰기
Home » 키밸류 옵저빙이란 » Page 3

키밸류 옵저빙이란

다음 gist는 앞서 소개한 Employee, Department의 to-many 관계의 KVO를 구현한 코드이다. 이 코드에는 Swift 상에서 KVO에 필요한 모든 코드와, to-many 관계에 대한 프록시를 사용하기 위한 모든 요건이 표현되어 있으니 참고가 되었으면 한다.


import Foundation
// MARK: Employee
class Employee: NSObject
{
@objc dynamic var salary:Int
var name: String
init(name:String, salary:Int) {
self.name = name
self.salary = salary
super.init()
}
}
// MARK: Department
class Department: NSObject
{
@objc dynamic var totalSalary: Int = 0
@objc var employees: [Employee] = []
private var totalSalaryContext = 0
/// 총 급여액을 업데이트한다. 새로 계산된 금액이 기존값과 다른 경우에만
/// 실제로 업데이트한다.
func updateTotalSalary() {
let newTotal = employees.map{ $0.salary }.reduce(0, +)
if newTotal != totalSalary {
willChangeValue(for:\Department.totalSalary)
totalSalary = newTotal
didChangeValue(for:\Department.totalSalary)
}
}
// 수동 통지를 위해서 totalSalary에 대한 자동통지를 중단한다.
override func automaticallyNotifiesObservers(forKey key: String) -> Bool
{
guard key == "totalSalary"
else {
return super.automaticallyNotifiesObservers(forKey: key)
}
return false
}
// MARK: collection accessors for employees
@objc(countOfEmployees)
func numberOfEmployees() -> Int {
return employees.count
}
@objc(objectInEmployeesAtIndex:)
func employee(at index:Int) -> NSObject { return employees[index] }
// MARK: collection mutators for employees
@objc(insertObject:inEmployeesAtIndex:)
func insertEmployee(_ employee:Employee, at index:Int) {
// 직원을 삽입할 때 salary에 대해 옵저빙한다.
employee.addObserver(self, forKeyPath: #keyPath(Employee.salary),
options: [.new],
context: &totalSalaryContext)
employees.insert(employee, at: index)
// 직원이 늘어나면 총급여는 증가하므로 업데이트한다.
updateTotalSalary()
}
@objc(removeObjectFromEmployeesAtIndex:)
func removeEmployee(at index:Int) {
// 직원을 제거하기 전에 해당 직원에 대한 salary 옵저빙을 그만둔다.
employees[index].removeObserver(self, forKeyPath: #keyPath(Employee.salary))
employees.remove(at: index)
// 직원이 줄었으므로 총급여를 업데이트한다.
updateTotalSalary()
}
// MARK: observer method
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard context == &totalSalaryContext
else {
super.observeValue(forKeyPath: keyPath,
of: object,
change: change,
context: context)
}
// 개별직원의 salary가 변경되면
// 총급여 역시 갱ㅇ신되어야 한다.
updateTotalSalary()
}
}
// MARK: Observer
class Foo: NSObject
{
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let key = keyPath,
key == #keyPath(Department.totalSalary)
else {
super.observeValue(forKeyPath: keyPath,
of: object,
change: change,
context: context)
return
}
guard let theObj = object as? Department
else {
return
}
NSLog("total salary has been changed \(theObj.totalSalary)")
}
}
let dept = Department()
let e1 = Employee(name: "e1", salary: 10)
let e2 = Employee(name: "e2", salary: 20)
let foo = Foo()
dept.addObserver(foo, forKeyPath: #keyPath(Department.totalSalary),
options: [.new], context: nil)
let eee = dept.mutableArrayValue(forKey: "employees")
NSLog("직원추가")
eee.add(e1)
NSLog("직원추가")
eee.add(e2)
NSLog("임금상승")
e1.salary = 30
NSLog("직원해고")
eee.removeObject(at: 0)
print(dept.totalSalary)


  1.   그렇기 때문에 Swift에서 옵저빙 가능한 프로퍼티는 @objc dynamic var 를 써서 선언하게 된다. 접근자가 런타임에 동적으로 결정되기 때문이다. 
Pages: 1 2 3