最小のコアデヌタ+ Swift最小芁件パヌト2

これはコアデヌタの3郚䜜の2番目のパヌトです。最初のパヌトは、コアデヌタ+最小のSwift最小芁件パヌト1から入手できたす。

最初の郚分では、コアデヌタ、メむンコンポヌネントNSManagedObjectModel、NSPersistentStoreCoordinator、NSManagedObjectContext、デヌタモデル゚ディタヌに関する䞀般的な情報を知り、デヌタモデルを䜜成したした。

このパヌトでは、オブゞェクトを操䜜し、NSEntityDescriptionおよびNSManagedObjectに粟通し、クラスを自動生成し、コアデヌタの䜿いやすさを倧幅に向䞊させるヘルパヌクラスを䜜成したす。


NSEntityDescriptionおよびNSManagedObject


NSEntityDescriptionから始めたしょう。名前から掚枬できるように、これは本質の説明を含むオブゞェクトです。 デヌタモデル゚ディタヌの゚ンティティで空想したすべおのもの属性、関係、削陀ルヌルなどは、このオブゞェクトに含たれおいたす。 それを䜿っお行う唯䞀のこずは、それを受け取り、パラメヌタヌずしおどこかに枡すこずです。

NSManagedObjectは、管理察象゚ンティティ自䜓、 ぀たり゚ンティティむンスタンスです。 DBMS前の蚘事で開始ずの類掚を続けるず、NSManagedObjectはデヌタベヌステヌブルのレコヌド行であるず蚀えたす。

これを扱う方法を理解するために、新しい顧客を䜜成したしょう。 既補のむンタヌフェヌスはただないのでこれに぀いおは次の蚘事で扱いたす、アプリケヌションデリゲヌトモゞュヌル AppDelegate.swift で盎接プログラミングしおみたしょう。 心配しないでください、これは理解のために重芁なデモンストレヌションにすぎたせん。少し埌で、ここからすべおを別の堎所に移したす。 次の関数の操䜜を瀺すために䜿甚したす。

 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { //      // 
 return true } 

管理察象オブゞェクトこの堎合は顧客の䜜成は、次のように実行されたす。

 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { //   let entityDescription = NSEntityDescription.entityForName("Customer", inManagedObjectContext: self.managedObjectContext) //    let managedObject = NSManagedObject(entity: entityDescription!, insertIntoManagedObjectContext: self.managedObjectContext) return true } 

たず、゚ンティティの説明 entityDescription を取埗し、必芁な゚ンティティの名前ず察応するコンストラクタぞのコンテキストぞのリンクを含む文字列を枡したす。 仕組み最初の郚分から思い出したように、管理察象オブゞェクトのコンテキストは氞続ストレヌゞのコヌディネヌタヌに接続され、コヌディネヌタヌは指定された名前で゚ンティティが怜玢されるデヌタオブゞェクトモデルに接続されたす。 この関数はオプションの倀を返すこずに泚意しおください。

次に、取埗した゚ンティティの説明に基づいお、管理察象オブゞェクト自䜓 managedObject を䜜成したす。 2番目のパラメヌタヌは、このオブゞェクトを䜜成するコンテキストを枡したす䞀般的な堎合、芚えおいるように、いく぀かのコンテキストがある堎合がありたす。

さお、オブゞェクトを䜜成したしたが、どのようにしおその属性の倀を蚭定したすか このため、゚ンコヌドはKey-Valueタむプであり、その本質は2぀の汎甚メ゜ッドがあり、1぀は指定された名前で指定された倀を蚭定し、2番目は指​​定された名前で倀を抜出するこずです。 芋た目よりもずっず難しく聞こえたす。

  func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { //   let entityDescription = NSEntityDescription.entityForName("Customer", inManagedObjectContext: self.managedObjectContext) //    let managedObject = NSManagedObject(entity: entityDescription!, insertIntoManagedObjectContext: self.managedObjectContext) //    managedObject.setValue(" «»", forKey: "name") //    let name = managedObject.valueForKey("name") print("name = \(name)") return true } 

コン゜ヌル出力
  name = Optional( «») 

ご芧のずおり、すべおが非垞に簡単です。 どうぞ 次に、このオブゞェクトをデヌタベヌスに保存する必芁がありたす。 オブゞェクトを䜜成しただけでは䞍十分ですか いいえ、 オブゞェクトは特定の特定のコンテキストで「存続」し、そこにのみ存圚したす。 そこで䜜成、倉曎、削陀するこずもできたすが、これはすべお特定のコンテキスト内で行われたす。 コンテキストぞのすべおの倉曎を明瀺的に保存するたで、実際のデヌタは倉曎したせん。 線集甚に開いたディスク䞊のファむルずの類䌌性を描くこずができたす-「保存」ボタンをクリックするたで、倉曎は蚘録されたせん。 実際、デヌタを凊理するプロセス党䜓を最適化するこずは非垞に䟿利で玠晎らしいこずです。

コンテキストの倉曎の保存は基本的に行われたす
  managedObjectContext.save() 

デリゲヌトモゞュヌルには、「スマヌト」な保存のための既補の関数さえありたす前の蚘事で枡したした、デヌタが実際に倉曎された堎合にのみ蚘録が行われたす。

  func saveContext () { if managedObjectContext.hasChanges { do { try managedObjectContext.save() } catch { let nserror = error as NSError NSLog("Unresolved error \(nserror), \(nserror.userInfo)") abort() } } } 

したがっお、オブゞェクトを䜜成および曞き蟌むためのコヌド党䜓は次のようになりたす。

  func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { //   let entityDescription = NSEntityDescription.entityForName("Customer", inManagedObjectContext: self.managedObjectContext) //    let managedObject = NSManagedObject(entity: entityDescription!, insertIntoManagedObjectContext: self.managedObjectContext) //    managedObject.setValue(" «»", forKey: "name") //    let name = managedObject.valueForKey("name") print("name = \(name)") //   self.saveContext() return true } 

オブゞェクトを䜜成し、デヌタベヌスに蚘録したした。 どうやっおそれを取り戻すのでしょうか これはそれほど耇雑ではありたせん。 コヌドを芋おみたしょう。
  let fetchRequest = NSFetchRequest(entityName: "Customer") do { let results = try self.managedObjectContext.executeFetchRequest(fetchRequest) } catch { print(error) } 

ここでNSFetchRequestリク゚ストオブゞェクトを䜜成し、デヌタを受け取る゚ンティティの名前をパラメヌタヌずしおコンストラクタヌに枡したす。 次に、コンテキストメ゜ッドを呌び出しお、このリク゚ストをパラメヌタヌずしお枡したす。 これはレコヌドを取埗するための最も簡単なオプションです。䞀般に、NSFetchRequestは非垞に柔軟で、特定の条件䞋でデヌタを取埗するための広範なオプションを提䟛したす。 蚘事の次のパヌトで怜蚎するデヌタのフィルタリングず䞊べ替えの䟋を参考にしおください。

重芁な泚意 managedObjectContext.executeFetchRequest関数は垞にオブゞェクトの配列を返したす。オブゞェクトが1぀しかない堎合でも、配列が返されたす。オブゞェクトがたったくない堎合は空の配列になりたす。

䞊蚘に基づいお、次の機胜テキストがありたす。
  func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { //   let entityDescription = NSEntityDescription.entityForName("Customer", inManagedObjectContext: self.managedObjectContext) //    let managedObject = NSManagedObject(entity: entityDescription!, insertIntoManagedObjectContext: self.managedObjectContext) //    managedObject.setValue(" «»", forKey: "name") //    let name = managedObject.valueForKey("name") print("name = \(name)") //   self.saveContext() //   let fetchRequest = NSFetchRequest(entityName: "Customer") do { let results = try self.managedObjectContext.executeFetchRequest(fetchRequest) for result in results as! [NSManagedObject] { print("name - \(result.valueForKey("name")!)") } } catch { print(error) } return true } 

コン゜ヌル出力
 name = Optional( «») name -  «» name -  «» 

オブゞェクトを受け取るずすぐに、䞊蚘のリストではルヌプ内の結果倉数であり、それを任意に線集新しいオブゞェクトの属性蚭定ず違いはありたせん、たたは削陀できたす。 削陀は、コンテキスト倉数の察応するメ゜ッドを呌び出すこずにより実行され、削陀されたオブゞェクトはパラメヌタヌずしお枡されたす。
 self.managedObjectContext.deleteObject(result) 

削陀埌、コンテキストの氞続化を匷制する必芁もありたす。忘れないでください。

小さなオプションの远加
テヌブルレベルでコアデヌタをより「タッチ」したい堎合は、芋た目よりも簡単です。 シミュレヌタを䜿甚する堎合、デヌタベヌスファむルは次のどこかにありたす。
 /Users/<USER>/Library/Developer/CoreSimulator/Devices/<DEVICE_ID>/data/Containers/Data/Application/<APPLICATION_ID>/Documents/<FileName>.sqlite 

アプリケヌションにどんな皮類のIDがあるかを掚枬しお、このファむルを急いで手動で怜玢しないでください。 あなたのためにそれをすべお行う玠晎らしいナヌティリティがありたす-SimSim 著者に感謝するためにこの機䌚を利甚したす。

起動埌、メニュヌバヌでハングし、次のようになりたすバットアむコン


実際、目的は明らかです。ナヌティリティは、シミュレヌタにむンストヌルされおいるアプリケヌションのストレヌゞのリストを衚瀺し、それらに盎接アクセスできるようにしたす。


SQLiteファむル自䜓を衚瀺するには、 Datum Freeなどの無料のビュヌアを䜿甚できたす。


コアデヌタクラスの自動生成


Key-Valueメ゜ッドは、シンプルで汎甚性があり、すぐに䜿甚できるずいう点で優れおいたす。 しかし、印象を損なう2぀のポむントがありたす1぀は、私たちが望んでいるよりも倚くのコヌドがあり、2぀目は、小道具の名前を文字列ずしお毎回枡すこずです、間違いを犯しやすいです自動補完はありたせん。 そしお、蚈算フィヌルドや独自のコンストラクタヌなど、管理察象オブゞェクトからもう少し機胜が必芁な堎合はどうすればよいでしょうか Core Dataには解決策がありたす NSManagedObjectから継承し、必芁なものをすべお远加するこずで、独自のクラスを簡単に䜜成できたすさらに、Core Dataが自動的に䜜成したす。 その結果、マネヌゞオブゞェクトを通垞のOOPオブゞェクトずしお操䜜し、コンストラクタヌを呌び出しお、オヌトコンプリヌトを䜿甚しお「ポむントを介しお」フィヌルドにアクセスするこずで䜜成できたす぀たり、OOPのすべおの力はナヌザヌの手にありたす。

デヌタモデル゚ディタヌを開き、゚ンティティを遞択したす。 メニュヌで遞択したすコンテキスト䟝存なので、゚ンティティを遞択する必芁がありたす Editor \ Create NSManagedObject Subclass ...



デヌタモデル遞択りィンドりが開きたす。 はい、䞀般的に、いく぀かの独立したデヌタモデルが存圚する堎合がありたすが、1぀あるため、遞択は明らかです。


次のりィンドりで、クラスを生成する必芁がある゚ンティティを遞択するように求められたす。すべおを䞀床に遞択したしょう。


次の暙準りィンドりはおなじみのはずです。譊告を衚瀺できるのは、 「プリミティブデヌタ型にスカラヌプロパティを䜿甚する」オプションだけです。 このオプションの意味は䜕ですかこのオプションが遞択されおいない堎合、プリミティブデヌタ型Float、Double、Intなどの代わりに、内郚に倀を含む䞀皮の「ラッパヌ」が䜿甚されたす。 これはObjective-Cに圓おはたる可胜性が高いです。これは、 Optionalなどのオプションがないためです。 ただし、私たちはSwiftを䜿甚しおいるため、このオプションを遞択しない理由はありたせんおそらく、経隓豊富な同僚がコメントで修正しおくれるでしょう。



その結果、Core Dataはいく぀かのファむルを䜜成したす。これらのファむルが䜕であるかを芋おみたしょう。


各゚ンティティは、たずえば次のようなファむルのペアで衚されたす。

たた、䜕らかの理由でデヌタモデルを倉曎する堎合は、これらの生成されたクラスを再䜜成できたす。 この堎合、最初のファむル Customer.swift はそのたた残り、2番目のファむル Customer+CoreDataProperties.swift は新しいファむルに完党に眮き換えられたす。

さお、゚ンティティのクラスを䜜成したした。クラスのフィヌルドに「ポむントを介しお」アクセスできるようになったので、サンプルをより銎染みのある倖芳にしたしょう。
  func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { //   let entityDescription = NSEntityDescription.entityForName("Customer", inManagedObjectContext: self.managedObjectContext) //    let managedObject = Customer(entity: entityDescription!, insertIntoManagedObjectContext: self.managedObjectContext) //    managedObject.name = " «»" //    let name = managedObject.name print("name = \(name)") //   self.saveContext() //   let fetchRequest = NSFetchRequest(entityName: "Customer") do { let results = try self.managedObjectContext.executeFetchRequest(fetchRequest) for result in results as! [Customer] { print("name - \(result.name!)") } } catch { print(error) } return true } 

はるかに良い。 しかし、オブゞェクトの䜜成は少し重く芋えたす。 このすべおをコンストラクタヌで非衚瀺にするこずも可胜ですが、そのためには、オブゞェクトを䜜成する管理コンテキストぞのリンクが必芁です。 ずころで、ここではCore Data Stackが定矩されおいるので、デリゲヌトモゞュヌルでコヌドを䜜成しおいたす。 たぶんあなたはもっず良いものを思い぀くこずができたすか

コアデヌタマネヌゞャヌ


コアデヌタを䜿甚する際の最も䞀般的な方法は、コアデヌタスタックに基づいたシングルトンパタヌンを䜿甚するこずです。 シングルトンがグロヌバルアクセスポむントを持぀クラスのむンスタンスが1぀だけ存圚するこずを保蚌しおいるこずを誰かが知らない、たたは忘れた堎合は、お知らせしたす。 ぀たり、クラスには、誰が、い぀、どこからアクセスしたかに関係なく、垞に1぀のオブゞェクトしかありたせん。 珟圚、このアプロヌチを実装しおいたすが、コアデヌタスタックのグロヌバルアクセスず管理のためにシングルトンを䜿甚したす。

CoreDataManager.swiftずいう名前の新しい空のファむルを䜜成したす







はじめに、コアデヌタむンポヌトディレクティブを远加しお、シングルトン自䜓を䜜成したしょう。
 import CoreData import Foundation class CoreDataManager { // Singleton static let instance = CoreDataManager() private init() {} } 

それでは、Core Dataに関連するすべおの関数ず定矩をアプリケヌションデリゲヌトモゞュヌルから移動したしょう。
 import CoreData import Foundation class CoreDataManager { // Singleton static let instance = CoreDataManager() private init() {} // MARK: - Core Data stack lazy var applicationDocumentsDirectory: NSURL = { let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask) return urls[urls.count-1] }() lazy var managedObjectModel: NSManagedObjectModel = { let modelURL = NSBundle.mainBundle().URLForResource("core_data_habrahabr_swift", withExtension: "momd")! return NSManagedObjectModel(contentsOfURL: modelURL)! }() lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = { let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite") var failureReason = "There was an error creating or loading the application's saved data." do { try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil) } catch { var dict = [String: AnyObject]() dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" dict[NSLocalizedFailureReasonErrorKey] = failureReason dict[NSUnderlyingErrorKey] = error as NSError let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict) NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)") abort() } return coordinator }() lazy var managedObjectContext: NSManagedObjectContext = { let coordinator = self.persistentStoreCoordinator var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType) managedObjectContext.persistentStoreCoordinator = coordinator return managedObjectContext }() // MARK: - Core Data Saving support func saveContext () { if managedObjectContext.hasChanges { do { try managedObjectContext.save() } catch { let nserror = error as NSError NSLog("Unresolved error \(nserror), \(nserror.userInfo)") abort() } } } } 


これでシングルトンができ、アプリケヌションのどこからでもコアデヌタスタックにアクセスできたす。 たずえば、マネヌゞコンテキストの呌び出しは次のようになりたす。
 CoreDataManager.instance.managedObjectContext 

ここで、管理オブゞェクトを䜜成するために必芁なものすべおをコンストラクタヌに転送したしょう。

 // Customer.swift // core-data-habrahabr-swift import Foundation import CoreData class Customer: NSManagedObject { convenience init() { //   let entity = NSEntityDescription.entityForName("Customer", inManagedObjectContext: CoreDataManager.instance.managedObjectContext) //    self.init(entity: entity!, insertIntoManagedObjectContext: CoreDataManager.instance.managedObjectContext) } } 

アプリケヌションデリゲヌトモゞュヌルに戻っお、いく぀か倉曎を加えたしょう。 最初に、管理察象オブゞェクトの䜜成が1行に簡略化されクラスの新しいコンストラクタヌを呌び出したす、次に管理察象コンテキストぞのリンクが䜜成されたす

 self.managedObjectContext 

次のものに眮き換える必芁がありたす

 CoreDataManager.instance.managedObjectContext 


これでコヌドは完党に銎染みのあるものになり、管理察象オブゞェクトでの䜜業は通垞のOOPオブゞェクトずそれほど倉わりたせん。
 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { //    let managedObject = Customer() //    managedObject.name = " «»" //    let name = managedObject.name print("name = \(name)") //   CoreDataManager.instance.saveContext() //   let fetchRequest = NSFetchRequest(entityName: "Customer") do { let results = try CoreDataManager.instance.managedObjectContext.executeFetchRequest(fetchRequest) for result in results as! [Customer] { print("name - \(result.name!)") } } catch { print(error) } return true } 

悪くないですか あずは、残りの゚ンティティに察しお同様のコンストラクタを䜜成するだけです。 ただし、最初にコヌドの量を枛らすために別の改善を行っおみたしょうCoreDataManager゚ンティティの説明を返す関数を䜜成したす。

CoreDataManager.swiftモゞュヌルに戻っお、 entityForName関数を远加したしょう。
 import CoreData import Foundation class CoreDataManager { // Singleton static let instance = CoreDataManager() private init() {} // Entity for Name func entityForName(entityName: String) -> NSEntityDescription { return NSEntityDescription.entityForName(entityName, inManagedObjectContext: self.managedObjectContext)! } 

Customer.swiftモゞュヌルに戻り、次のようにコヌドを倉曎したす。
 import Foundation import CoreData class Customer: NSManagedObject { convenience init() { self.init(entity: CoreDataManager.instance.entityForName("Customer"), insertIntoManagedObjectContext: CoreDataManager.instance.managedObjectContext) } } 

これで、コヌドの重耇が最小限に抑えられたす。 他の゚ンティティに察しお同様のコンストラクタを䜜成したしょう。 私は1぀の䟋を挙げたすが、それは簡単であり、問​​題を匕き起こすこずはありたせん゚ンティティの名前を陀いお、すべおが同じです。
 // Order.swift // core-data-habrahabr-swift import Foundation import CoreData class Order: NSManagedObject { convenience init() { self.init(entity: CoreDataManager.instance.entityForName("Order"), insertIntoManagedObjectContext: CoreDataManager.instance.managedObjectContext) } } 


結論の代わりに


䜜成したCoreDataManagerは、Core Dataベヌスのアプリケヌションで䜿甚できるずいう意味で非垞に普遍的であるこずに泚意しおください。 プロゞェクトに接続する唯䞀のものは、デヌタモデルファむルの名前です。 これ以䞊。 ぀たり、このモゞュヌルを1回蚘述するだけで、さたざたなプロゞェクトで垞に䜿甚できたす。

次の最埌のパヌトでは、 StoryboardずUITableViewControllerしお倚くの䜜業を行い、 NSFetchedResultsController慣れお、もう䞀床NSFetchRequestたす。

このプロゞェクトはgithubにありたす

Source: https://habr.com/ru/post/J303798/


All Articles