Last active
January 10, 2018 03:30
-
-
Save sooop/e9602d34bbce8bbcb5f26d6f4de1af8b to your computer and use it in GitHub Desktop.
to-many relationship에 대한 KVO 처리방법
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment