본문 바로가기

학습 노트/iOS (2021)

195. Faulting & Uniquing

Faulting


iOS 앱에서 DB를 사용하는 경우,
모든 경우에 저장소에 접근해 DB를 CRUD 하는 것이 아니라
Context에 불러 온 뒤 CRUD를 마치고 저장소를 업데이트 하는 방식을 사용한다.
이를 더 세분화 하면

Context에는 데이터 자체가 전달 되는 것이 아닌 정보가 전달되고,
데이터의 원본은 RowCache에 전달된다.
이후 Context에서 실제 데이터에 접근을 시도 하는 경우,
Context에 존재하는 정보를 통해 RowCache의 데이터를 불러오게 된다.

이렇게 Context에 저장되는 정보를 Place Holder Object, Managed Object Fault 라고 부르며,
해당 정보를 사용해 RowCahce의 데이터를 불러 오는 과정을 Firing Faults, Fetch Actual Value 라고 부른다.

이는 DB 내에서도 동일하게 작동한다.

만약 Employee Entity를 불러 온다면 지금 당장은 Relationship 관계에 있는
Department Entity를 불러 오지 않는다.
이러한 상태의 Department Entity는 Relationship Fault 라고 부른다.
마찬가지로 Fault 상태의 Department Entity에 접근할 때 Department를 가져오게 되므로,
메모리를 효율적으로 사용할 수 있다.
단, Department에서 데이터를 가져올 때 접근한 속성만 가져 오는 것이 아닌
Department Entity 전체를 불러오게 된다.

  • Department Entity PlaceHolder의 속성에 접근
  • RowCache 확인
  • RowCache에 존재하는 경우 Cache에서 Department Entity Load
  • RowCache에 존재하지 않는 경우 저장소에서 Load, Chache에 저장

즉 위와 같은 순서로 동작하게 된다.

Fault Firing은 매우 빠른 속도로 이루어지지만
여전히 데이터를 로드하는 무거운 작업에 속하기 때문에,
여러 객체에서 단시간에 많은 Fault Firing이 이루어 지면 Overhead가 발생할 수 밖에 없다.
이를 More Faulting Overhead라고 부른다.

따라서 값을 불러오는 시점에 이미 존재하는 Managed Object에서 값을 불러 올 수 있도록 설정할 수 있고,
이를 Less Faulting Overhead라고 부른다.

Fault Firing 이후 불러 온 데이터가 더이상 필요 없게 되면,
지속해서 메모리를 점유하도록 두는 것 보다는 정리해 주는 것이 더 효율적이다.
따라서 해당 객체를 메모리에서 삭제해 다시 Fault로 전환 하는 것도 가능하다.
단, 데이터 무결성을 유지할 수 있도록 진행하지 않는 Update가 있는지 확인 한 뒤 전환해야 한다.

 

Uniquing


데이터의 무결성을 유지하기 위한 기술이다.

위의 Emplyee Entity에서 Department의 속성에 접근하려는 경우
Fault 상태의 Department를 Firing 한다.
마찬가지로 아래의 Employee Entity가 Department의 속성에 접근한다면,
두 Deparment가 독립된 객체라면 문제 없이 위의 과정을 한 번 더 반복하면 되지만,
같은 Department 객체라면 최종적으로 생성된 결과 중 어느 것이 올바른지 모호해 진다.
이러한 데이터 무결성의 문제를 해결하기 위해 Uniguing을 지원하고,
Uniguing은 한 Context 내에서 중복으로 등록되지 않도록 방지하도록 작동한다.
Uniquing은 Context에 내장 된 기능이다.

class FaultingTableViewController: UITableViewController {
   
   var list = [NSManagedObject]()
   
   func fetchAllEmployee() -> [NSManagedObject] {
      let context = DataManager.shared.mainContext
      var list = [NSManagedObject]()
      
      context.performAndWait {
         let request = NSFetchRequest<NSManagedObject>(entityName: "Employee")

         let sortByName = NSSortDescriptor(key: "name", ascending: true)
         request.sortDescriptors = [sortByName]
         
         request.fetchLimit = 100
         
         do {
            list = try context.fetch(request)
         } catch {
            fatalError(error.localizedDescription)
         }
      }
      
      return list
   }

Table View에 Employee Entity의 데이터를 표시한다고 가정 했을 때,
Scene에 처음 진입하는 시점에 100개의 데이터를 Fetch하게 된다.
이후 Table View를 내리게 되면 표시 되지 않던 데이터들이 필요하게 되고,
이 시점에는 위와 같이 Core Data Fetch가 아닌 Faults로 동작해,
Sotrage를 거치지 않고, Cache의 Data를 받아오게 된다.

request.returnsObjectsAsFaults = false

이러한 동작은 Fetch Request에서 기본적으로 동작하고,
위와 같이 returnsObjectsAsFaults 속성을 False로 바꿔 주는 것으로 비활성화 해
Fault가 아닌 실제 객체를 전달하도록 설정할 수 있다.

DataManager.shared.mainContext.refresh($0, mergeChanges: false)

또한 fetch로 받아 온 데이터에 대해 refresh 메서드를 호출하고,
두번째 파라미터에 false를 전달하면 이미 저장 된 객체에 대한 메모리 할당을 해제하고,
Fault로 변경할 수 있다.

'학습 노트 > iOS (2021)' 카테고리의 다른 글

197. Batch Processing with CoreData  (0) 2022.06.03
196. Data Validation  (0) 2022.05.30
194. Transformable  (0) 2022.05.20
193. Fetched Result Controller  (0) 2022.04.09
192. Expression  (0) 2022.03.30