Tag: Swift 4

掌握CoreData(第8部分:验证核心数据)

核心数据 ,使您可以将验证逻辑放入托管对象模型中并指定最常见的约束,而不是在代码中编写验证逻辑。 将数据转储到持久性存储之前 ,您可以对对象应用验证。 如果对象遵循约束,则上下文数据将成功持久保存,否则它将引发错误。 添加约束是开发人员的责任。 该框架具有许多用于在将对象持久化到磁盘之前对其进行验证的API。 在本教程中,我将向您展示Core Data为开发人员提供的验证对象的选项。 在第2部分中,我们写了这一点 托管对象上下文(MOC) 它提供缓存,更改跟踪,延迟加载,重做,撤消和验证功能 验证在核心数据中如何工作 验证约束仅在保存操作期间或在请求时由Core Data应用。 (您可以在对您的应用程序流程有意义的任何时候直接调用验证方法) 入门 您可以在此处下载启动程序项目。 该项目是教程的继续,请参见第5部分和第7部分,我们在其中创建了该项目。 如果您遵循以前的教程,请先删除该应用程序,然后再开始执行任何操作 属性验证 转到CrudOperationCoreData.xcdatamodeld→点击用户实体→双击firstName属性→在右侧的模型检查器中添加验证。 我们添加了firstName的两个验证最小和最大长度,还添加了默认值,如图1所示。确保选中了最大和最小长度复选框。 让我们深入研究代码并检查验证是否有效。 如您所见,当我们创建用户实体时, 默认值被分配给firstName属性,并且当我们使用12个以上的字符更新firstName值时,它在内存中的上下文中成功更新,但是当我们尝试将更改提交到持久性存储时,它将检查首先进行验证,如果验证失败,则会抛出错误,如图2所示 。 您可以在对实体本身调用save方法throw validateForInsert()和validateForUpdate()实例方法之前请求验证,并且可以覆盖此方法以对代码应用自定义验证。 我们将在本部分的最后一部分中查看自定义验证 日期验证 您可以在Date上应用最小和最大验证,如图3所示。 确保应选中“最小和最大日期”复选框,否则验证将不起作用。 关系验证 关系与属性没有很大的不同。 它们也可能受到约束。 关系可以是可选的,也可以是必需的。 一对多关系的计数可以限制为最小值和最大值 。 转到CrudOperationCoreData.xcdatamodeld →在用户实体上点击→在任务关系属性上点击→在​​模型检查器中,添加最小和最大验证,如图4所示。 首先删除该应用程序,并在User对象上添加三个 任务 ,如图5所示。 当我们尝试保存它时。 因为我们为单个User对象限制(两个)添加了max task对象,所以我们将添加三个,这将引发如图6所示的错误。 它在内存中的上下文上成功添加了三个任务,但是当我们尝试将更改(使用save()方法)提交给持久性存储时,它将首先检查验证,如果验证失败,则会抛出错误,如图6所示 。 您可以在对实体本身调用save方法throw validateForInsert()和validateForUpdate()实例方法之前请求验证,并且可以覆盖此方法以对代码应用自定义验证。 我们将在本部分的最后一部分中查看自定义验证 正则表达式验证 现在,我们将探讨如何在模型检查器核心数据中添加正则表达式。 我们将对用户的firstName属性应用正则表达式,该属性将允许至少一个字符,并且该字符将为小写 […]

Swift 4.0中基于代码的用户界面

作为iOS开发人员,我一直渴望编写尽可能动态和可重用的代码。 在这篇文章中,我想向您介绍我的基于代码的UI的范例和实践。 证明这一点的最佳方法是通过一个简单的示例: 假设我们要显示一个图像作为视图控制器视图的背景。 同样,第一种最明显的方法是使用Xcode为我们提供的出色IB: 将UIImageView元素拖到视图中。 设置图像 应用顶部,底部,左/前导,右/后约束。 设置并连接您的插座。 Wallah-有了,您的真棒背景!

无限标签栏

最近,我被要求从我们的一个应用程序中删除一个较旧的框架,并用更新的(兼容iPhone X的)替代品替换它。 当我开始创建替代产品时,我们公司将计划转移到其他方面,但是我不想失去项目最后几周的工作/更新,以使所有工作顺利进行。 这就是为什么我将向您展示我本周工作的绝妙内容! (已完成的Repo Link在底部!) 好的,所以首先创建一个单视图应用程序(对于我的演示,我将其命名为新的标签栏)。 您要在主视图控制器中添加一个collectionview和一个Container View。 我将容器视图设置为将尾随,前导和顶部与安全区域对齐,并将展位空间与集合视图对齐。 我的收藏夹视图约束设置为:高度60,将尾随,前导和底部对齐到安全区域,顶部空间对齐到容器视图。 继续并删除设置Container View Controller时获得的defualt view controller。 在您的collectionView单元格中添加一个imageView和一个标签。 将图像视图的约束设置为:到单元格的尾随空间和前导空间10(消除了一个名为tabCell的类),顶部空间等于单元格,底部空间至标签。 将标签约束设置为:尾随前导底部到单元格,高度为15,顶部到imageView。 然后选择您的collectionView并将“滚动方向”设置为“水平”。 并且您的单元格大小为60X60。 将重用标识符设置为“ tabCell” 然后将您的Outlets设置到ViewController(我的名为MainViewController),并在viewDidLoad中设置您的collectionView委托和dataSource。 创建对MainViewController的扩展,以熟悉协议。 现在在DataSource中,只需为numberOfItemsInSection返回1并在cellForItemAtIndex中返回UICollectionViewCell()。 创建一个名为TabCollectionViewCell的新类,并通过联播为您的标签单元分配该类。 为imageView和Label设置插座。 完成后,它应该看起来像这样(请注意,我的标签渲染图未显示,因此我将其突出显示。): 现在我们都准备开始编程了!! 我个人想先解决最小的问题,所以我将设置TabCollectionViewCell。 覆盖您的awakeFromNib并准备调用名为updateWithImage的函数的prepareForReuse。 您的updateWithImage应该包含一个可选图像,并将该图像设置为tabImageView.image属性。 如果没有传递任何内容,则将其设置为nil。 由于我们正在使用自定义选项卡,因此我创建了一个选项卡模型(因为我很有创造力,因此命名为选项卡!)我将类Tab设置为具有tabName:String和tabImageName:String,并设置init来保存这些新值。 是时候进入我们的MainViewController了。 设置一个私有Tab变量,并使其成为Tab列表,一个私有selectedIndex来保存我们的索引选择。 并在didLoad视图中将selectedIndex设置为0。同样在viewDidLoad中,在设置collectionViewDelegate和Datasource之前,向选项卡列表调用一个新函数addingTabs。 在您的addTabs函数中设置您的主选项卡,然后设置一些选项卡(请注意,在我的项目中,我将其主题设置为超级护林员,并添加了一些经典以及较新的超级护林之一。 将您的numberOfItemsInSection更新为tabs.count,并更新cellForItemAt indexPath以返回tabCell(确保将collectionView.dequeueReusableCell(withReuseIdentifier:“ tabCell”,用于indexPath)用作TabCell)。 由于将所选索引的颜色设置为彩色更好,因此我们应该在此处进行设置。 我将选定的“索引”标签设置为蓝色,没有选定的标签为黑色。 在您的Delegate扩展中,创建didSelectItemAt indexPath并将您的selectedIndex设置为indexPath.row。 返回到您的selectedIndex声明,并向其添加didSet,以调用新函数setViewController()。 在setViewController(私有函数)中,我们将处理将容器视图更新为所选视图控制器的操作。 首先创建一个var viewController = UIViewController(),这将是我们viewController的占位符以及我们将更新的所有值。 然后打开带有默认显示UIViewController的selectedIndex。 我将所有视图控制器都放在导航控制器中,因此可以设置标题和navigationItem按钮。 现在,您可能会问我们将如何跟上视图控制器的发展,为此,我有一个很棒的答案,我已经引用了几次。 […]

🍦去吃冰淇淋!🍦

为了迎接一年一度的七夕情人节特别隆重巨献奉上各式各样各样甜腻可人的冰淇淋青菜萝卜东坡肉〜各人有各爱请自行挑选口味吧 一只不够也可以吃两只还可以内部服外敷保证让你打从心里冷遍全身心如槁灰内附隐藏版彩蛋冰淇淋喔敬请好好吸收喔〜 moon0411 / 0817-iceCream 人们可以在GitHub上构建软件。 超过2千8百万的人使用GitHub来发现,发掘和贡献超过…… github.com 这次主要使用程式码+ Storyboard ID识别的方式来串连两页资料不过写完作业感到特别饿还是让我先出门吃支冰淇淋先 1.首页: getViewController •storyboard放置面 •action&outlet拉线完成,准备开始写程式噜〜 •struct iceCream型别•宣告数组ice []并编写内容•在IB动作func buttonPressed()写入要指定串连的控制器是哪个(用故事板ID识别) •数组。 randomElement() • present() @IBAction func buttonPressed(_ sender:Any){ 如果让controller = Storyboard?.instantiateViewController(withIdentifier:“ iceCream”)为? iceCreamIntroViewController { 让图片= ice.randomElement() controller.iceimage =图片 目前(控制器,动画:true,完成:nil) } 2.次页: iceCreamIntroViewController •动作与插座拉线•宣告可选变数 (注意:这次的变数名称取得不太好,以致到后来我自己都糊涂了,指称范围应该要更广更直觉一些,以免误会设定的变数内容) var iceimage:iceCream? •在viewDidLoad()写入次页想显示出的内容 如果让iceimage = iceimage { showImageView.image = UIImage(名称:iceimage.image) titleLabel.text =“ […]

在Swift 4中探索可编码和核心数据

也可以在我的投资组合网站PXPGraphics.com上阅读此博客文章。 最近,我曾在LinkedIn和Twitter上询问iOS社区,是否创建允许Core Data在Swift 和 Objective-C中支持Codable的框架是否有价值: 为了简化此数据流,应在应用程序体系结构中将数据,缓存和持久性逻辑抽象到它们自己的层中-这是提出的框架Badger(?)的目的 ,我希望在2019年发布。例如,提议的框架的一些目标包括: JSON序列化 模型验证 对象图管理 内存中缓存 磁盘上的持久性 记录和错误处理 由于该框架承担着许多责任,因此我决定将这些组件分解为本博客系列,标题为Exploring Codable and Core Data 。 为了帮助降低读者的入门门槛,我决定将重点放在每个帖子的几个基本主题上。 该系列将包括以下主题: Codable入门 使用可解码的解码自定义类型 使用可编码的自定义类型编码 (快来了) 核心数据入门 (快来了) 使核心数据成为您的模型层 (快来了) 创建您的核心数据栈 (快来了) 将可编码模型保存到核心数据 (快来了) 从核心数据获取可编码模型 (快来了) 测试可编码模型 (快来了) 测试核心数据模型 (快来了) 注意:我坚信以这种方式共享知识可以帮助来自不同经验和背景的各个级别的读者学习和提高他们的技能,并帮助社区发展壮大。 因此,您的任何反馈,问题或意见将不胜感激。 在此先感谢您,并随时关注更新!

在Swift中探索内存安全和独占访问

在Swift中,我们有一个非常有趣的概念,它称为SE-176上提出并在Swift 4中实现的“ 对内存的独占访问”。但这甚至意味着什么? 好吧,互斥访问是一个规则,强制所有对变量的潜在修改(写)必须与对该变量的任何其他访问互斥。 例如,在多线程环境中工作时,我们有多个线程访问一个变量,并且一个或多个线程可以修改该值,而该变量已被另一个变量修改,则无法从任何读取器或写入器访问该变量。作家。 您可以在提案文档中阅读有关此内容的更多信息。 如何保证独家访问? 通常,如果您知道一个变量将被多个线程修改并读取,则可能会在此调用周围使用NSLock或NSRecursiveLock,以确保任何时候在该代码区域中只有一个线程处于活动状态。 因此,问题是…如果我们锁定了对变量的更改(写),那么数据竞争将如何发生,实际上,在这种情况下,甚至没有任何写操作发生? 🤔 那是我所谈论的有趣的行为。 对于值类型的变异函数,发生的事情是将self作为inout参数隐式传递给该函数。 在内存访问的上下文中, inout参数具有写访问权限,该访问权限自动从函数的开头开始,并在函数完成时结束。 因此,在该示例中,即使我们不对函数内部的数组进行突变,整个方法的执行也具有长期的写访问权限。 从Apple文档中… 函数可以对其所有输入输出参数进行长期写访问。 在评估了所有非输入参数之后,才开始对输入输出参数进行写访问,并持续该函数调用的整个过程。 如果有多个输入/输出参数,则写入访问的开始顺序与参数出现的顺序相同。 您可以在“ 对In-Out参数的访问冲突”部分的“ Memory Safety [1]”文档中找到此内容。 因此,当我们在第一个队列上调用node.clearChilds()对self进行写访问时(也就是节点),然后在第二个队列上再次尝试对node.clearChilds()进行访问时,就会发生数据争用。节点,并尝试在self.childs.isEmpty调用中读取self 。 解决方案非常简单。 如果我们知道对变量函数的写访问已经开始,那么我们将需要锁定此方法的调用方。 因此,我们可以确保对该内容的专有访问权:)) 让我们修复代码,看看会发生什么 似乎一切正常,没有更多的数据争夺了:)) 上面示例中解决此问题的另一种方法可能是将Node类型更改为类而不是结构,因此您没有这种行为,因为在快速类中没有变异方法,而self只是隐式传递给该功能,因此不会启动长期写访问。 当我们在多线程环境中工作时,我们大多数Swift开发人员basically的日常生活基本上是什么,这些概念和细节对于了解非常有用,并且可以使我们避免很多怪异的bug并节省很多调试时间时间。 在下面的参考部分中,有很多关于内存安全,锁,线程安全以及我们在这里探讨的所有其他主题的精彩内容。 这就是本文的全部,希望您喜欢🙂 如果我有问题或您有任何意见或疑问,请告诉我。 我很高兴收到您的反馈feedback 您可以在Twitter上@ LucianoPassos11找到我。 感谢您阅读🙂 Swift编程语言(Swift 4.1):内存安全。 https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/MemorySafety.html [swift-evolution] [评论] SE-0176:强制对内存进行独占访问。 https://github.com/apple/swift-evolution/blob/master/proposals/0176-enforce-exclusive-access-to-memory.md 2017年10月27日,星期五问答:锁,线程安全和Swift:2017年版。 https://www.mikeash.com/pyblog/friday-qa-2017-10-27-locks-thread-safety-and-swift-2017-edition.html 主人处的swift / OwnershipManifesto.md·apple / swift https://github.com/apple/swift/blob/master/docs/OwnershipManifesto.md

提示:将代码从Swift 3升级到Swift 4?

在转换代码之前执行此操作 提示#1 如果您的viewcontroller符合任何协议,则必须将其与类定义一起声明,以便Xcode可以将协议方法转换为相应的swift版本。 例如,我们可以直接从Storyboard分配UITableView / UICollectionView Delegate和Datasource,通常不在类定义中声明它,而直接编写协议方法,其他委托如UISearchBar,UITextView或UITextField就是这种情况。 只要确保您的ViewController包含声明 例如 MyViewController类:UIViewController,UITableViewDelegate,UITableViewDataSource 为什么要这样做? Xcode可能不会将已经实现的方法定义转换为最新的方法定义,因为它可能会将其视为您的本地函数。 您需要通知Xcode您的ViewController符合协议XYZ,因此Xcode也会更新您的方法定义,或者至少会给您相应的警告或错误 结果呢? 如果您没有在UnitTesting中涵盖所有代码,或者尚未实现UITesting来彻底测试应用程序,则很有可能在进行手动测试时会丢失部分代码。 这可能会导致崩溃或不当行为。 这也将确保Swift的未来版本在进行转换时会自动转换您的委托方法。 更好的方法(如果您仍然不遵循) 始终为协议实现创建类的扩展 例如 扩展MyViewController:UICollectionViewDelegate,UICollectionViewDataSource { func numberOfSections(在collectionView:UICollectionView中)-> Int { 返回1 } func collectionView(_ collectionView:UICollectionView,numberOfItemsInSection部分:Int)-> Int { 返回10 } func collectionView(_ collectionView:UICollectionView,cellForItemAt indexPath:IndexPath)-> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier:“ reuseIdentifier”,用于:indexPath) 返回单元 } func collectionView(_ collectionView:UICollectionView,didSelectItemAt indexPath:IndexPath){ } 提示2 如果您的代码是Swift和Objective-C的混合体,则Swift […]

Swift4 11日关闭(2)

尾随闭包 如果需要将很长的闭包表达式作为最后一个参数传递给函式,使用尾随闭包来增强函式的初始化性。尾随闭包是写在小括号后面的闭包表达式,函式支持将闭包作为最后一个参数做呼叫 func函式名称((内部参数名:()->()){ 内部执行程序 } //不使用尾随闭包时呼叫功能 函式名称({ 闭包内的程式 }) //闭包型别为:()->()没有参数也没有回传值,可以看到闭包是作为参数放在()里//使用尾随闭包时呼叫功能 函式名称(){ 闭包内的程式 } //如果函式只有闭包这一个参数,可以省略() 函式名称{ 闭包内的程式 } 捕获值 闭包可以在其定义的中间中捕获常数或变数,即使定义这些常数或变数的原使用区域已不在,闭包仍可以在闭包函式体内使用或修改这些值 Swift中,可捕获值最简单的形式是巢状函数,也就是定义在其他函式中的函式。巢状函式可以捕获并访问外部函式内部所有的参数及其锁定亦得常数及变数,,即使巢状函数已回传,导致这些常数或变数的做用已已,闭包仍可对这些已捕获的值做操作。 巢状函式的内部存取了runningTotal及金额两个变数,是因为他从外部函式捕获了这两个变数的参考,而此捕获参考可以runningTotal及金额呼叫完makeIncrementer完后不会消失,并在下一呼叫增量时,runningTotal仍存在 呼叫这个函式的方法: 再上面的呼叫中,可以验证下一呼叫功能时,runningTotal仍存在的事实,因此再另外呼叫一次增量5及增量10,重新验证此表达式 闭包是参考型别 上面的例子中,incrementTen及crementFive都是常数,但这些常数指向的闭包仍可以增加其捕获的变数值 逃逸闭包 当我们宣告一个函式的参数为闭包时,可以再参数的类型前面写@时,我们可以说这个闭包逃逸了,而它可以在函式回传后被调用。逃避表示允许闭包逃逸。 闭包可以逃逸的一种方式为:被保存定义于函数外的变数里。 newFunction会接收一个闭包当做参数,并添加到我们在函数外宣告的Array中,而我们让闭包逃逸意味着要在闭包中用self 自动闭包 是一种自动被建立的闭包,用于包装后传递给函式作为参数的表达式,这种闭包没有参数,当被使用时,会回传被包装在里面表达式的值。简单来说,自动闭包是一种简化后的语法,让我们可以用普通的表达式代替显式的闭包,靴子省略了大括号{}。 自动闭包可以让你延迟求值,我们可以控制程序何时才进行求值: 我们删除了aArray中的第一个元素,但在还没调用removeArray时,aArray的数量仍是6个,也就是延迟求值,如果闭包永远不被调用,那闭包里的表达式就永远不会求值 ・ Swift4 Day-01基本语法・ Swift4 Day-02选项及无 ・ Swift4 Day-03集合类型—数组 ・ Swift4 Day-04集合类型—设置 ・ Swift4 Day-05集合类型—字典 ・ Swift4 Day-06控制流程(1) ・ Swift4 Day-07控制流(2) ・ Swift4 […]

掌握CoreData(第7部分核心数据关系删除规则)

正如我们在第5部分和第6部分中讨论的,当我们创建实体之间的关系时。 每个实体NSManagedObject与一个实体描述(NSEntityDescription的一个实例)相关联,该实体描述提供有关对象的元数据。 对象元数据包括对象代表的实体的名称以及其属性和关系的名称。 创建关系时,应考虑许多事项,删除规则就是其中之一。 删除规则 删除规则是使用Core Data的一大便利。 每个关系都有删除规则。 它定义了删除拥有关系的记录时发生的情况。 要么 关系的删除规则指定了尝试删除源对象时应该发生的情况。 在这一部分中,我们将使用之前的实体来了解删除规则。 下载Todo应用程序。 首先我们将做一些理论上的部分,然后我们将使用实际编码对其进行测试 核心数据支持四种删除规则:“层叠”,“拒绝”,“无效”和“不执行操作”。 级联 删除源时,请删除关系目标处的对象。 如果我们删除用户,则与该用户关联的所有任务也将被删除 要添加实体之间的关系,请参阅第5部分和第6部分。转至受管理对象模型文件,并使User → Task关系级联,如图1所示。 让我们创建一个具有两个todo 任务的 U ser ,如图2所示。如果您不理解此代码,请参阅第5部分和第6部分,其中我逐步讨论了该代码。 此代码将创建一个具有两个与之关联的待办事项的用户 ,并将其保存到磁盘 受管对象上下文视觉映射如图3所示。 通过仅向用户调用删除代码。 由于用户 → 任务删除规则是通过删除用户对象而级联的,因此它将删除与其相关联的所有任务 。 如您在控制台上看到的,没有任务对象剩余。 删除操作后的受管对象上下文的可视化映射如图4所示。您可以看到, 用户和与其关联的两个任务也从上下文中删除,并且通过调用save()方法,它也将从持久性存储中删除。 无效化 如果将关系的删除规则设置为Nullify ,则在删除记录时该关系的目标将无效,但目标对象仍然存在,仅源和目标之间的关系将被删除 首先将级联关系从下拉菜单更改为Nullify ,如图5所示。 让我们创建一个具有两个待办事项的U ser ,如图2所示。如果您不理解此代码,请参阅第5和第6部分,其中我逐步讨论了该代码。 此代码将创建一个具有两个与之关联的待办事项的用户 ,并将其保存到磁盘 托管对象上下文的可视化映射将如图6所示。 通过删除User对象,它不会删除与用户相关的任务,因为您可以看到我们仍添加在Managed Object Context中并打印在控制台中的两个任务,如图7所示。 托管对象上下文的可视化映射如图8所示。您可以看到因为User → Task具有相反的关系,在应用删除代码之前Task也引用了User对象。在删除User对象之后,此关系设置为nil如下所示 拒绝 […]

掌握CoreData(第6部分,Core Data CRUD操作中实体之间的关系)

出于本部分的目的,我们将扩展Todo应用程序。 在这一部分中,我们将研究如何在架构中执行CRUD操作,在架构中有许多实体,并且一个实体与另一实体有关系。 请参阅第5部分,以了解我们将要工作的项目中的实体及其之间的关系 创建记录到核心数据 将记录添加到具有实体之间关系的核心数据的过程具有以下任务 从appdelegate单例对象引用persistentContainer 从persistentContainer创建/访问单例托管对象上下文 使用Task创建第一个实体,并为每个属性设置值 使用Task创建第二个实体,并为每个属性设置值 使用Passport创建一个实体并为每个属性设置值 使用User创建一个实体,并为每个属性设置值 我们刚创建的两个任务与用户对象相关联,代表了与用户相关的任务。 由于使用了用户→任务关系,所以使用了一对多NSSet 我们刚刚创建的与用户对象关联的护照,代表与用户关联的护照。 由于使用了用户→任务关系为一对一属性 将其保存到磁盘(永久存储) 通过调用save将保存到直接存储在文档中的sqlite磁盘文件中,您可以通过调用NSPersistentContainer上的静态方法defaultDirectoryURL来找到位置,如图1,2和3所示,我们刚刚创建的记录存储在磁盘上。 从核心数据中获取记录 从具有实体之间关系的核心数据中获取记录的过程具有以下任务 从appdelegate单例对象引用persistentContainer 从persistentContainer创建/访问单例托管对象上下文。 如您所见,我们每次都在做这项重复性工作,我们将在以后的部分中使用Core Data构建持久层时解决此问题。 创建了一个提取请求,以告知核心数据堆栈仅查看名称为Task的那些实体 执行我们的提取请求,它将从持久性存储中返回Task对象的数组 迭代刚从核心数据堆栈中获取的任务对象 以面向对象的样式打印任务详细信息 由于我们创建了逆关系,因此我们也可以访问User属性。 这是逆关系的好处之一。 如您所见,我们使用Task对象引用访问user.firstname属性。 由于逆关系, 任务对象还持有用户对象引用。 最后,我们可以使用Task对象访问Passport信息。 由于逆关系 。 在后面的部分中,我们将看到使用逆关系的更多好处 从核心数据中删除记录 从核心数据中删除具有实体关系的记录的过程具有以下任务 从appdelegate单例对象引用persistentContainer 从persistentContainer创建/访问单例托管对象上下文。 如您所见,我们每次都在做这项重复性工作,我们将在以后的部分中使用Core Data构建持久层时解决此问题。 创建了一个提取请求,以告知核心数据堆栈仅查看名称为User的那些实体 执行我们的提取请求,它将从持久性存储中返回User对象的数组 迭代刚从核心数据堆栈中获取的User对象 在上下文上使用delete()方法删除用户 由于User → Task删除规则是Cascade,因为我们删除了User对象。 它将自动删除所有与用户相关的任务 ,如图5所示,任务计数在数据库中返回0个任务对象。 由于User → Passport删除规则是“ Cascade”,因为我们删除了User对象,它将自动删除与用户相关联的护照信息,并且还返回0 […]