<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                > 原文出處: http://chengway.in/post/ji-zhu/core-data-by-tutorials-bi-ji-si Raywenderlich家[《Core Data by Tutorials》](http://www.raywenderlich.com/store/core-data-by-tutorials)這本書到此為止已經回顧過半,今天來學習一下第六章“版本遷移”。第六章也是本書篇幅最多的。根據數據模型的每一次的調整程度,數據遷移都有可能會變得更加復雜。最后,遷移數據所花的成本甚至超過了所要實現的功能。那么前期完善對Model的設計將會變得十分重要,這一切都需要開發者去權衡。 ## **Chapter 6: Versioning and Migration** 本章提供了一個記事本APP,未來數據結構要變更,遷移(migration)過程就是:在舊data model的基礎上將數據遷移到新的data model中來。 ### **一、When to migrate** 如果僅僅是把Core data當做是離線緩存用,那么下次update的時候,丟棄掉就OK了。但是,如果是需要保存用戶的數據,在下個版本仍然能用,那么就需要遷移數據了,具體操作是創建一個**新版本的data model**,然后提供一個*遷移路徑(migration path)*。 ### **二、The migration process** 在創建Core Data stack的時候,系統會在添加store到persistent store coordinator之前分析這個store的model版本,接著與coordinator中的data model相比較,如果不匹配,那么Core Data就會執行遷移。當然,你要啟用*允許遷移*的選項,否則會報錯。 具體的遷移需要源data model和目的model,根據這兩個版本的model創建mapping model,mapping model可以看做是遷移所需要的地圖。 遷移主要分三步: 1. Core Data拷貝所有的對象從一個data store到另一個。 2. Core Data根據**relationship mapping**重建所有對象的關系 3. 在destination model開啟數據有效性驗證,在此之前的copy過程中是被disable了。 這里不用擔心出錯,Core Data只有遷移成功,才會刪除原始的data store數據。 作者根據日常經驗將遷移劃分為四種: * Lightweight migrations * Manual migrations * Manual migrations * Fully manual migrations ~~~ 第一種是蘋果的方式,你幾乎不用做什么操作,打開選項遷移就會自動執行。第二種需要設置一個mapping model類似與data model,也是全GUI操作沒什么難度。第三種,就需要你在第二種的基礎上自定義遷移策略(NSEntityMigrationPolicy)供mapping model選擇。最后一種考慮的是如何在多個model版本中跨版本遷移,你要提供相應的判定代碼。 ~~~ ### **三、A lightweight migration** 所謂輕量級的遷移就是給Note實體增加了一個image的屬性。要做的步驟也很簡單: 1. 在上一model基礎上創建UnCloudNotesDataModel v2,然后添加image屬性。 2. 啟用Core Data自動遷移選項,這個選項在**.addPersistentStoreWithType方法**中開啟 > 作者的做法是在CoreDataStack初始化的時候傳入這個options數組參數,然后再傳遞給.addPersistentStoreWithType方法。 ~~~ init(modelName: String, storeName: String, options: NSDictionary? = nil) { self.modelName = modelName self.storeName = storeName self.options = options } store = coordinator.addPersistentStoreWithType( NSSQLiteStoreType, configuration: nil, URL: storeURL, options: self.options, error: nil) lazy var stack : CoreDataStack = CoreDataStack( modelName:"UnCloudNotesDataModel", storeName:"UnCloudNotes", options:[NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true]) ~~~ **NSMigratePersistentStoresAutomaticallyOption**是自動遷移選項,而**NSInferMappingModelAutomaticallyOption**是mapping model自動推斷。所有的遷移都需要mapping model,作者也把mapping model比作是向導。緊接著列出了可以應用自動推斷的一些模式,基本上都是對實體、屬性的增、刪、改以及關系的修改。 > 1. Deleting entities, attributes or relationships; > 2. Renaming entities, attributes or relationships using the renamingIdentifier; > 3. Adding a new, optional attribute; > 4. Adding a new, required attribute with a default value; > 5. Changing an optional attribute to non-optional and specifying a default value; > 6. Changing a non-optional attribute to optional; > 7. Changing the entity hierarchy; > 8. Adding a new parent entity and moving attributes up or down the hierarchy; > 9. Changing a relationship from to-one to to-many; > 10. Changing a relationship from non-ordered to-many to ordered to-many (and vice versa). 所以正確的做法就是任何數據遷移都應先從自動遷移開始,如果搞不定才需要手動遷移。 ### **四、A manual migration** 1. 與lightweight migration相同,首先要創建一個UnCloudNotesDataModel v3,這次需要添加一個新Entity,命名為Attachment,并給該Entity添加兩個屬性dateCreated、image。將Note和Attachment的關系設為一對多,即一個note會有多個attachment。 2. 創建一個**mapping model**,命名為UnCloudNotesMappingModel_v2_to_v3 3. 修改mapping model,分為**Attribute Mappings**和**Relationship Mappings** ![](https://box.kancloud.cn/2015-08-21_55d6eafb36e8f.jpg) 上圖是實體**Note**的**mapping model**,這里的source指的是源數據模型(data model)里的Note實體,創建新加實體**Attachment**的*mapping model*也很簡單,在Entity Mapping inspector里將**source entity**改為**Note**,接著實體Attachment的屬性dateCreated、image就來自于上一版data model里的Note實體。 ![](https://box.kancloud.cn/2015-08-21_55d6eb010a342.jpg) 在Mapping model中可以添加過濾條件,比如設置NoteToAttachment的**Filter Predicate**為image != nil,也就是說Attachment的遷移只有在image存在的情況下發生。 4. Relationship mapping,這里要注意的一點就是實體Note與Attachment的關系是在UnCloudNotesDataModel v3這一版本中添加的,所以我們需要的*destination relationship*其實就是UnCloudNotesDataModel v3中的relationship。于是我們這樣獲得這段關系 ![](https://box.kancloud.cn/2015-08-21_55d6eb0123eca.jpg) 作者這里展示了這個表達式函數: ~~~ FUNCTION($manager, "destinationInstancesForEntityMappingNamed:sourceInstances:", "NoteToNote", $source) ~~~ 5. 最后需要更改之前CoreData的*options*設置 ~~~ options:[NSMigratePersistentStoresAutomaticallyOption:true, NSInferMappingModelAutomaticallyOption:false] ~~~ 將自動推斷mapping model關掉,因為我們已經自定義了mapping model。 ### **五、A complex mapping model** 1. 創建一個UnCloudNotesDataModel v4的版本,在v3的版本上增加一個Entity,命名為**ImageAttachment**,設為*Attachment*的子類。接著為這個新的ImageAttachment添加caption、width、height三個屬性,移除Attachment中的image。這樣就為今后支持videos、audio做好了擴展準備。 2. 添加UnCloudNotesMappingModel_v3_to_v4,和上一節類似,**NoteToNote mapping**和**AttachmentToAttachment mapping**Xcode已經為我們設置OK了,我們只需關注**AttachmentToImageAttachment**,修改他的$source為**Attachment** ![](https://box.kancloud.cn/2015-08-21_55d6eb0158727.jpg) 除了從父類*Attachment*繼承而來的屬性,新添加的三個屬性都沒有mapping,我們用代碼來實現吧。 3. 除了mapping model中的*FUNCTION expressions*,我們還可以自定義**migration policies**。增加一個NSEntityMigrationPolicy類的swift文件命名為AttachmentToImageAttachmentMigrationPolicyV3toV4,覆蓋NSEntityMigrationPolicy初始化方法: ~~~ class AttachmentToImageAttachmentMigrationPolicyV3toV4: NSEntityMigrationPolicy { override func createDestinationInstancesForSourceInstance( sInstance: NSManagedObject, entityMapping mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool { // 1 創建一個新destination object let newAttachment = NSEntityDescription.insertNewObjectForEntityForName("ImageAttachment", inManagedObjectContext: manager.destinationContext) as NSManagedObject // 2 在執行手動migration之前,先執行mapping model里定義的expressions for propertyMapping in mapping.attributeMappings as [NSPropertyMapping]! { let destinationName = propertyMapping.name! if let valueExpression = propertyMapping.valueExpression { let context: NSMutableDictionary = ["source": sInstance] let destinationValue: AnyObject = valueExpression.expressionValueWithObject(sInstance, context: context) newAttachment.setValue(destinationValue, forKey: destinationName) } } // 3 從這里開始才是custom migration,從源object得到image的size if let image = sInstance.valueForKey("image") as? UIImage { newAttachment.setValue(image.size.width, forKey: "width") newAttachment.setValue(image.size.height, forKey: "height") } // 4 得到caption let body = sInstance.valueForKeyPath("note.body") as NSString newAttachment.setValue(body.substringToIndex(80), forKey: "caption") // 5 manager作為遷移管家需要知道source、destination與mapping manager.associateSourceInstance(sInstance, withDestinationInstance: newAttachment, forEntityMapping: mapping) // 6 成功了別忘了返回一個bool值 return true } } ~~~ 這樣就定義了一個自定義遷移policy,最后別忘了在AttachmentToImageAttachment的**Entity Mapping Inspector**里*Custom Policy*那一欄填入我們上面創建的這個*UnCloudNotes.AttachmentToImageAttachmentMigrationPolicyV3toV4*。 ## **六、Migrating non-sequential versions** 如果存在多個版本非線性遷移,也就是可能從V1直接到V3或V4...這又該怎么辦呢,這節代碼比較多,說下思路,就不全帖出來了。 1. 創建一個DataMigrationManager,這個類有一個stack屬性,由他來負責提供合適的migrated Core Data stack。為了分清各個版本,這個manager初始化需要傳入store name和model name兩個參數。 2. 擴展NSManagedObjectModel,創建兩個類方法: ~~~ class func modelVersionsForName(name: String) -> [NSManagedObjectModel] class func uncloudNotesModelNamed(name: String) -> NSManagedObjectModel ~~~ 前者根據model名稱返回所有版本的model,后者返回一個指定的Model實例。 > When Xcode compiles your app into its app bundle, it will also compile your data models. The app bundle will have at its root a .momd folder that contains .mom files. MOM or Managed Object Model files are the compiled versions of .xcdatamodel files. You’ll have a .mom for each data model version. 3. 根據上面擴展的方法,繼續對NSManagedObjectModel進行擴展,創建幾個比較版本的handle method,例如: ~~~ class func version2() -> NSManagedObjectModel { return uncloudNotesModelNamed("UnCloudNotesDataModel v2") } func isVersion2() -> Bool { return self == self.dynamicType.version2() } ~~~ 直接使用“==”比較當然是不行的,這里繼續對“==”改寫一下,有同樣的entities就判定相等: ~~~ func ==(firstModel:NSManagedObjectModel, otherModel:NSManagedObjectModel) -> Bool { let myEntities = firstModel.entitiesByName as NSDictionary let otherEntities = otherModel.entitiesByName as NSDictionary return myEntities.isEqualToDictionary(otherEntities) } ~~~ 4. 增加store和model是否匹配的判斷方法,這里主要用NSPersistentStoreCoordinator的**metadataForPersistentStoreOfType方法**返回一個**metadata**,然后再用model的**isConfiguration方法**對這個*metadata*進行判斷,來決定model和persistent store是否匹配。 5. 添加兩個計算屬性,**storeURL**和**storeModel**,storeModel遍歷所有的model,通過第4步的判斷方法找出相匹配的storeModel。 6. 修改stack的定義:先判斷,store與model不相容,就先執行遷移。 ~~~ var stack: CoreDataStack { if !storeIsCompatibleWith(Model: currentModel) { performMigration() } return CoreDataStack(modelName: modelName, storeName: storeName, options: options) } ~~~ 7. 自定義一個遷移方法,將store URL、source model、destination model和可選的mapping model作為參數,這就是完全手動實現遷移的方法。如果做輕量級的遷移,將最后一個mapping model設為nil,那么使用本方法和系統實現沒有差別。 ~~~ func migrateStoreAt(URL storeURL:NSURL, fromModel from:NSManagedObjectModel, toModel to:NSManagedObjectModel, mappingModel:NSMappingModel? = nil) { //...... } ~~~ 8. 最后我們來實現第6步提到的**performMigration**方法,現在最新的版本是v4,開始之前先做個判斷,當前model的最新版本為v4,才執行這個performMigration方法下面的內容: ~~~ if !currentModel.isVersion4() { fatalError("Can only handle migrations to version 4!") } ~~~ 這樣就變成了從v1 -> v4,v2 -> v4,v3 -> v4的遷移,接下來的方法也很簡單,分別判斷storeModle的版本號,執行第7步的*migrateStoreAt:*方法,并且通過對*performMigration*方法的**遞歸調用**來最終遷移到v4版本。 作者最后還給了兩條建議: * 盡量可能采取最簡單的遷移方式,因為遷移很難測試。 * 每個版本都盡量保存一點數據以便將來遷移時可以測試。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看