Tag: 核心数据

核心数据代码生成说明!

从Xcode8开始,Apple在Xcode数据模型编辑器中添加了新的Codegen设置,以帮助开发人员管理和维护其NSManagedObject子类。 Codegen设置包含3种可能的配置: 手动/无 类定义 类别/扩展 最初,这3种配置可能看起来有些混乱,我发现实际上很难找到一篇好的文章来解释每种配置之间的区别以及使用哪种配置。 在本文中,我将详细介绍每种配置,并分析每种配置之间的差异,并提出一些建议,说明应该在核心数据项目中使用哪种配置以及如何使用它。 事不宜迟,让我们开始吧! 这是Xcode8之前的默认行为,开发人员必须手动创建和维护NSManagedObject子类的更改。 您可以尝试创建一个示例核心数据项目,添加一个新的实体调用TestEntity并按照此链接中的步骤生成NSManagedObject子类。 如果正确执行此步骤,Xcode将生成2个文件: TestEntity + CoreDataClass.swift TestEntity + CoreDataProperties.swift 生成这两个文件后,您应该可以在项目中使用TestEntity 。 尝试将以下代码行复制到您的应用程序委托中,您的项目应编译没有任何错误。 让_ = TestEntity(context:persistentContainer.viewContext) 在数据模型编辑器中创建实体时,此配置是默认的Codegen配置。 使用此配置,Xcode将自动生成所需的NSManagedObject子类,作为项目派生数据的一部分。 要查看实际情况,请打开先前创建的样本核心数据项目,然后删除两个TestEntity+CoreDataClass.swift 和 TestEntity+CoreDataProperties.swift 。 您会注意到,删除两个文件后,您的项目将不再能够编译。 这是因为您对Codegen使用了手动/无配置。 现在尝试将Codegen配置更改为Class Definition,您会注意到您的项目现在可以再次编译而没有任何错误。 要查看自动生成的文件,请按cmd +单击TestEntity ,然后选择“跳转到定义”。 右键单击打开的TestEntity+CoreDataClass.swift然后选择“在Finder中显示”。 现在您应该可以同时看到TestEntity+CoreDataClass.swift 和 TestEntity+CoreDataProperties.swift (由Xcode自动生成) 在查找器窗口中。 但是请注意,这两个文件均不在您的Xcode项目中,实际上它们都位于Xcode的“派生数据”文件夹中。 切勿编辑自动生成的文件,因为它们由Xcode管理,因此,每次构建项目时,它们都会被Xcode覆盖。 可以在类定义和手动/无之间描述此配置。 Xcode将仅自动生成TestEntity+CoreDataProperties.swift 为您,您将必须管理和维护TestEntity+CoreDataClass.swift 你自己。 将示例项目的Codegen配置更改为Category / Extension,您将注意到在派生数据中,Xcode不再自动生成TestEntity+CoreDataClass.swift 对您来说,您的项目将无法再次编译。 要解决该错误,您可以使用上述相同的方法来请求Xcode为您的项目创建TestEntity+CoreDataClass.swift ,但是Xcode还将为您的项目创建TestEntity+CoreDataClass.swift的副本,因此请确保将其删除从项目中删除,以避免与派生数据文件夹中的自动生成版本冲突。 希望上面的解释使您对每种Codegen配置的工作方式有更清晰的了解。 […]

Swift,安全又简单的另一个CoreData堆栈

微调NSPersistentContainer 我时不时地看到苹果公司的代码示例和模板提出了非最佳解决方案。 我发现苹果的代码有问题,CoreData在我列表的顶部。 您可以自己尝试。 创建包含CoreData的任何Xcode项目。 苹果公司的CoreData代码建议使用主线程(视图上下文)来保留数据。 最重要的是,它在AppDelegate中完成。 但是,Apple确实使用这种方法解决了写入冲突,因为我们仅使用一个上下文来保存数据,但是解决写入冲突还不够。 持久数据,无论保存多少数据,都比读取慢得多。 保存数据会执行SQLite COMMIT,即使您只写一个BYTE,也要花费几毫秒的时间。 在主线程上编写将使您的用户界面变得迟钝。 这就是为什么我将CoreData堆栈组织成这样的原因: 我假设您了解有关Apple的NSPersistentContainer全部知识,如果不了解,则需要进行检查。 您需要先设置它并加载存储,然后再创建CoreData堆栈。 在上面的代码中,我在其他文章中使用了一个简单的“变异运算符”。 在我们的基本CoreData堆栈中,我们有3个专用上下文: mainContext只读主上下文,出于所有与UI相关的目的,它会自动观察更改。 writingContext读+写背景上下文,我们只在这里和这里保存数据,其他人只是观察。 readingContext只读的背景上下文,用于非UI用途,当我们必须对后台的更改做出反应时(并非每个人都需要此内容)。 我们从不对后台上下文使用performAndWait ,因为我们的块是在这些上下文的内部队列中异步执行的。 当我们想“关闭”核心数据栈时,我们需要等待内部队列完成后台任务。 这就是为什么我们将背景上下文包装到BackgroundManagedObjectContext并在deinit使用performAndWait取消所有未完成的任务的原因。 除此之外,应使用performTask扩展方法(这是对perform方法的包装)添加所有任务。 苹果提供了在此设置中应避免的方法 func performBackgroundTask(_块:@转义(NSManagedObjectContext)->无效) 问题是,如果使用此方法进行写操作,则会发生写冲突,因为每次都会使用其自己的内部串行队列创建一个新的上下文。

CoreData中的值类型

坚持使用CoreValue 是否可以仅使用值类型来实现业务模型对象? 使用CoreData保留它们怎么办? 在这篇文章中,我将研究CoreValue。 一种轻量级框架,旨在为完成此类任务提供支持。 我已经构建了一个小应用程序来对其进行测试。 这是Github中的代码。 使用值类型对对象进行建模。 在我开始谈论CoreValue之前,有两个潜在问题值得一提: 双向关联无法使用值类型完全表示。 我写了 另一篇 关于这个问题的文章。 结构不支持继承。 关联必须是单向的,以便仅使用值类型正确表示。 例如,如果您需要一部电影来了解其导演,而导演则需要了解这部电影。 仅使用结构表示是不可能的。 您可以忍受这些限制并仍然代表模型对象吗? 如果真是这样,那么仅值类型方法可能对您有效。 否则,您需要课程。 核心价值 它是围绕CoreData的轻量级包装框架。 它负责将值类型unboxing到CoreData对象中,以及将CoreData对象unboxing到值类型中。 它还包含简单的抽象,以便于查询,更新,保存和删除 该框架允许您使用结构/枚举,使用它们,然后保存/删除/获取它们。 当需要将它们发送到托管上下文时,它将值转换为托管对象。 反之亦然。 真的很酷的主意。 它是如何工作的? 您定义结构和枚举。 然后,您遵循框架的协议之一,例如CVManagedPersistentStruct 。 基本上,您可以指定如何从NSManagedObject获取值类型的实例。 每次尝试保存值时,框架都会将其转换为NSManagedObject 。 当您尝试从数据库中获取一个对象时,框架将使用objectID来获取托管对象。 然后它将使用您的转换函数来获取值类型实例。 好的,让我们看看当您仅用值类型表示模型对象并尝试通过CoreValue使用CoreData实现持久性时会发生什么。 开始 集成非常容易,可以进行pod安装,导入CoreValue,然后就可以编写代码了。 但是,我确实遇到了一些非常早期的问题。 第一个问题是尝试保留第一个值类型时发生编译错误 。 使用咖喱将数据从NSManagedObject到值类型中: 无法将类型’((NSManagedObjectID ?, String,Director,Genre)-> Movie’的值转换为预期的参数类型’(_)->(_)throws-> _’ 如果尝试尝试curry init与另一个持久值类型连接的类型,则会发生这种情况。 在我的示例中, Movie知道其Director 。 Director是一个持久的CVManagedPersistentStruct,在Movie出现编译错误。 […]

Swift协议扩展方法调度

今天,我想写一个我发现的错误。 当我接触CoreValue时,我正在研究它。 CoreValue是一个框架,允许您使用CoreData保留值类型。 该错误导致数据库中的条目重复 。 该错误也很有趣,因为我学到了一些东西。 它使用语言功能作为隐藏的斗篷。 一旦找到了怪物,我就能看到这种语言特征并对其进行了解。 然后,我决定写这则短篇小说。 我学到了什么? 协议扩展有2种方法。 需求 方法和静态 方法 ( “没有需求支持”)。 需求方法使用动态调度 。 静态方法使用静态调度 。 知道它们之间的差异非常重要,这样您就不会产生错误。 我将通过首先解释该错误来说明为什么它很重要。 您可以跳过此部分,并了解以下每种方法。 我将不使用CoreValue示例,因为它很难解释。 所以,我要编一个故事。 英雄中隐藏的错误 这是一个关于不太聪明的英雄的故事。 英雄就是战士。 和战士,他们只是fight(). 那是必要条件 。 战斗人员在战斗时只会掷punch() 。 这是用扩展方法编码的,因为它是所有Fighter的共同行为: 协议战斗机{ func fight() //要求 } 扩展战斗机{ //需求 func fight(){ self.punch() } //静态方法(协议中不需要) funcpunch(){ 打印( “打拳” ) } } 英雄不是简单的战士,他们是MagicalFighters。 当魔法战士猛击时,他们首先施法: 协议MagicalFighter:战斗机{ […]

掌握CoreData(第14部分,多线程并发策略,父级-子级上下文)

父/子管理对象上下文 如图1所示,自iOS 6起,有一个更好,更优雅的策略。 父/子托管对象上下文的概念是, 子托管对象上下文依赖于其父托管对象上下文 将其更改保存到相应的持久性存储中。 实际上, 子托管对象上下文无法访问持久性存储协调器 。 每当保存子托管对象上下文时, 它包含的更改将被推送到父托管对象上下文 。 无需使用通知即可将更改手动合并到主或父托管对象上下文中。托管对象上下文可以嵌套。 子托管对象上下文可以具有自己的子托管对象上下文。 相同的规则适用。 此方法由Apply推荐 连接到持久性存储协调器的上下文应该是真理的唯一来源,这意味着应该在主线程上创建它,它负责处理与UI相关的任务。 如何实现并发 您可以看到如图2所示的简单并发体系结构。应注意的几点 子上下文负责使用某些后台线程执行繁重的任务 由于子上下文正在使用后台线程,因此此繁重的任务不会阻止主线程。 父NSMainQueueConcurrencyType应该在NSMainQueueConcurrencyType上创建 子NSPrivateQueueConcurrencyType是在NSPrivateQueueConcurrencyType上NSPrivateQueueConcurrencyType 当子项ManagedObjectContext完成繁重的任务时,核心数据也会自动将更改合并到父项中,我们只需要仅在子项上调用save方法 子ManagedObjectContext也可以在其中包含其他子上下文 使用此策略回顾上一部分 创建子托管对象上下文与到目前为止所看到的稍有不同。 子托管对象上下文使用不同的初始化程序initWithConcurrencyType:。 初始化程序接受的并发类型定义了托管对象上下文的线程模型。 NSMainQueueConcurrencyType →受管对象上下文只能从主线程访问。 如果尝试从任何其他线程访问它,则会引发异常。 NSPrivateQueueConcurrencyTypea →创建并发类型为NSPrivateQueueConcurrencyType的托管对象上下文时,该托管对象上下文与私有队列相关联,并且只能从该私有队列进行访问。 Apple引入父/子托管对象时,Core Data框架中添加了两种关键方法 上下文, perform :和performAndWait :这两种方法都可以使您的生活更加轻松。 在托管对象上下文上调用perform :并传入要执行的代码块时,Core Data确保该块在正确的线程上执行。 对于NSPrivateQueueConcurrencyType并发类型,这意味着将在该受管对象上下文的专用队列上执行该块。 数据流 如图3所示。“用户列表”屏幕显示了屏幕中的所有用户。 它从主线程上的父上下文中获取用户数据。 由于它的唯一目的是与UI进行交互,因此此上下文无法执行任何繁重的处理任务。 如图3所示,当前数据库中只有4个用户 现在UserListScreenViewController需要来自服务器的数据。 它打入网络电话,新的996用户响应。 它将在该线程上进行解析,并且不会影响主线程 。 用户仍然可以与UI交互,因为我们没有阻止主线程 。 […]

核心数据:自定义NSManagedObject和CRUD服务演示

这个故事基本上是了解我们如何在核心数据中执行基本的CRUD操作,即 C :创建实体 R :检索实体 U :更新实体 D :删除实体 让我们一步一步地做 创建一个新的Xcode项目,然后选择“ Use Core Data”,如下所示 之后,您将看到“ CoreDataDemo.xcdatamodelId ”,即“ ProjectName.xcdatamodelId ”。 点击它,然后选择下面的添加新实体按钮 您将获得一个空白实体,双击该实体将其重命名为“ Employee”。 现在添加属性/表字段 1.名称字符串 2. empId字符串 3.联系Integer 64 4.地址字符串 5. emailId字符串 Employee表看起来像 现在,在属性检查器中,您将看到带有下拉菜单的类选项,选择“ 手动/无”。 我们需要NSMangedObject Class的子类将值从类对象映射到数据库实体。 转到编辑器,然后选择创建 NSMangedObject子类。 之后,您将获得要选择的模型选项,选择CoreDataDemo ,然后选择需要子类的表,再选择Employee表。 您将看到2个名为 1. Employee + CoreDataClass.swift 2. Employee + CoreDataProperties.swift Congratts !!! 您已成功创建表的子类。 创建一个名为EmployeClass的模型类,该模型类用于在视图控制器中显示员工数据。 像这样添加变量 类EmployeeClass:NSObject […]

核心数据及其基本技术

什么是核心数据? 核心数据是用于管理应用程序中的模型层对象的框架。 您可以使用Core Data作为框架来保存,跟踪和修改iOS应用程序中的数据,但是您必须了解Core Data不是数据库。 核心数据使用SQLite,因为它是持久存储,但框架不是数据库。 核心数据管理对象图,跟踪数据中的更改并执行许多其他操作。 实际上,Core Data减少了为支持模型层而编写的代码数量。 它可以轻松地自动管理和更新内存中的对象,并处理复杂的对象图,而不会影响设备的内存管理。 在本文中,我们9series 是顶级iOS应用程序开发公司 ,在这里我们将讨论核心数据,对象模型和操作等的基本技术。 托管对象模型 核心数据功能取决于您创建的用于描述应用程序的实体,属性以及它们之间的关系的架构类型。 核心数据使用称为Managed Object Model的模式,这是NSManagedObjectModel的示例。 简而言之,核心数据模型越丰富,对支持您的应用程序越有帮助。 现在,让我们创建一个带有核心数据的单视图iOS应用程序,选中该复选标记以查看核心数据的基础知识。 当您创建一个带有核心数据选中标记的项目时,您会看到两个值得注意的变化,例如, demo.xcdatamodeld的新文件 Appdelegate文件包含核心数据堆栈代码 Appdelegate具有应用程序的持久性容器。 此实现创建并返回一个已为应用程序加载商店的容器。 新的demo.xcdatamodeld充当我们要保存的数据的模型层。 我们可以像其他任何关系数据库一样轻松地从UI添加实体,属性和关系。 将新记录添加到核心数据 将记录添加到核心数据的过程包括以下步骤: 需要引用一个持久容器 创建上下文 添加新实体 添加新记录 设置所有键的记录值 让我们在ViewController中编写所有内容。 在AppDelegate中设置容器后,我们现在需要引用该容器。 让我们为用户保存数据: 现在我们已经在托管对象中设置了数据。 是时候将其保存在数据库中了。 我们必须使用do try and catch块来编写它。 从核心数据中获取数据 要从数据库中获取对象,您将使用NSFetchRequest访问该数据。 从核心数据中获取对象是此框架的最主要特征。 在此示例中,您可以使用NSFetchRequest开始,该请求解释了要返回的数据。 我们可以使用此代码从实体中获取数据。 观察托管对象的上下文 核心数据框架具有多个API,这些API可让我们实现类似NSFetchedResultsController类的行为。 如果在托管对象上下文中发生任何更改,则框架本身会使用通知来通知我们。 发生更改时,每个受管对象上下文都会触发三种类型的通知: contextObjectsDidChange contextWillSave contextDidSave […]

创建消息过滤器扩展

苹果在iOS 11中引入了IdentityLookup,现在允许开发人员过滤SMS和MMS消息。 并非每条消息都会通过您的扩展程序,IdentityLookup仅在收到来自未知发件人的消息时才将查询传递给过滤器应用程序扩展程序。 另一个限制是不允许您将任何数据从扩展名写入共享容器。 有关IdentityLookup的更多详细信息,请查看Apple Docs。 要创建扩展,您要做的就是创建一个新的iOS应用,然后从Xcode菜单中选择File / New / Target,然后选择Message Filter Extension。 在您的扩展中,将自动为您创建一个从ILMessageFilterExtension继承的类。 它还符合ILMessageFilterQueryHandling协议,该协议有一个要求,即handle(_ queryRequest:context:completion 🙂方法。 在这种方法中,类型为ILMessageFilterQueryRequest的查询请求对象将传递给您,您将可以访问消息的发件人和messageBody 。 应用过滤规则后,需要调用完成闭包,该闭包将使用ILMessageFilterQueryResponse类型的单个参数,该参数需要创建并设置其action属性。 如果要防止显示该消息,则需要将操作类型设置为.filter。 其他两个选项.none和.allow将无效,该消息将正常显示。 这就是创建消息筛选器扩展所要做的全部。 以下是来自“垃圾邮件过滤器” SMS应用程序的示例,该应用程序使用CoreData共享容器加载用户创建的关键字列表,这些关键字用于过滤邮件正文: 编码愉快!

在CoreData中使用ValueTransformer加密用户的敏感信息

简单的方法! 对于那些正在使用核心数据并且: 有一天,您公司的某个部门要求您更改存储在SQLite中的所有字符串以进行加密 或者只是希望您的用户数据能从越狱的设备中安全 如果正确使用Core Data,它将是一个非常方便的应用程序框架。 但是大多数时候人们没有! 这就是为什么您听到有人喊着让我们开始使用Realm的原因,甚至从根本上说,为什么不编写我们自己的SQLite包装器。 好吧,将我们的希望投向新技术并认为新的lib可以解决当前的所有问题始终是我们的本性。 如果您正在使用具有多个人的应用程序,并且经常更新模型文件,那么幸运的是,轻量级迁移,映射模型崩溃会早晚找到您! 这将在我的下一篇文章中进行解释! 现在,让我们解决我们的问题! 为了简单起见,使用最少的代码即可达到我在此所讲的内容,我们将首先从模板创建一个新的Master and Detail App,然后在Xcode 9中检查核心数据。 检查您的核心数据模型应该是这样的。 继续并在模拟器中运行此代码,并在其中添加少量数据。 今天早上,您的安全部门说,如果不对用户数据进行加密,我们就永远不要将其存储在SQLite中,因为如果它是越狱的设备,那么任何人都可以看到该数据! 首先,苹果设备不容易被黑客入侵! 如果是越狱的设备,大多数情况下只有/曾经/希望越狱的用户! 好吧,可能还有其他情况,让我们仅加密织补数据! 让我们开始创建数据模型的下一个版本! 创建新版本的核心数据模型! 可转换意味着将其存储为数据,与二进制的区别在于调用了上下文的save:和fetch:,可转换将在过程中使用ValueTransformer进行额外的操作。 在这里,我们使用一个名为“ EncryptionTransformer”的类。 现在,让我们验证我们的工作是否有效,打开SQLite文件,我们应该看到这些基数为64的“ ==”: 工作完成了! 我开始写一些可以在iOS开发中使用的隐藏技巧。 苹果没有告诉您的内容(WADTY)。 告诉我你们最想知道的内容也将很有帮助! 敬请关注! (只需点击跟随按钮!)

掌握CoreData(第13部分,多线程并发策略通知)

Core Data支持两种流行的策略,即通知和父子托管对象上下文。 在本部分中,我们将仅查看Notifications。Apple不建议使用此策略,因此我们将不做进一步的介绍。 通知类型 托管对象上下文发布了三种类型的通知,以通知对象在托管对象上下文中发生的更改 NSManagedObjectContextObjectsDidChangeNotification:当托管对象上下文的一个托管对象已更改时,将发布此通知 NSManagedObjectContextWillSaveNotification:在托管对象上下文执行保存操作之前发布此通知 NSManagedObjectContextDidSaveNotification:在托管对象上下文执行保存操作后发布此通知 注意:仅当您在上下文上调用save()方法时,这些通知才会发布。 当您调用save()方法WillSaveNotification和NDidSaveNotification通知 肯定会调用,而DidChangeNotification仅在您实际在上下文中进行了某些更改时才调用。 当托管对象上下文通过持久性存储协调器将其更改保存到持久性存储时,其他托管对象上下文可能希望了解这些更改。 这很容易做到,甚至更容易将更改包含或合并到另一个托管对象上下文中。 注意:如果您正在执行NSBatchUpdateRequest / NSBatchDeleteRequest,这些通知将不会触发( 暂时忽略) 处理通知 在此处下载启动项目,如果您已经先删除了该应用程序 我们可以通过在NotificationCenter添加观察者来观察这些通知,如图1所示。 如您在图2中看到的,我们处理了DidChangeNotification 只。 notifications只是整个故事的一半。 如您所见,所有观察者方法都是通过通知对象传递的。 通知对象具有一个userInfo实例属性,该属性在Dictionary中保存所有信息。 这些都是用于检索更改的数据的键 NSUpdatedObjectsKey密钥包含所有更新的NSManagedObject 。 NSInsertedObjectsKey密钥包含所有插入的NSManagedObject 。 NSDeletedObjectsKey密钥包含所有已删除的NSManagedObject 。 NSRefreshedObjectsKey密钥包含所有刷新的NSManagedObject 。 插入盒 如您所见,我们在图3中插入了User对象。 执行save()方法时将触发通知 正如您在图4中看到的那样,由于我们在图3中插入了对象,这意味着将首先DidChangeNotification上下文中的更改,而DidChangeNotification将触发,并且我们对此进行了处理,这就是在控制台中打印出插入对象的原因。 之后, WillSaveNotification和DidSaveNotification通知将依次触发,并且由于我们未在选择器方法中执行任何操作,因此将不会发生任何事情。 如图4所示,我们在图3中插入的用户被打印在控制台上。 更新案例 如图5所示,我们获取了刚才插入的User对象,并在我们调用save()方法时更新了它的值。Notification handler打印了Updated值。 我们通过执行以下操作来完成此操作 从appdelegate单例对象引用persistentContainer 从persistentContainer创建/访问单例托管对象上下文 添加了托管对象上下文观察器以侦听上下文事件 我们刚刚从持久性存储中插入的Fetched User并更新了它的firstName和secondName属性,如图5所示。 在主上下文中使用save方法执行对持久性存储的提交 通知DidChangeNotification 方法在控制台中打印更新的值 之后, WillSaveNotification和DidSaveNotification通知将依次触发,并且由于我们未在选择器方法中执行任何操作,因此将不会发生任何事情 […]