我记得计算机科学课程的第一课Java继承。 🙂 在我们的学校里,我们的程序员受到了教授,面向对象改变了近几十年来开发软件的方式。 继承是最受灌输的概念之一。 基本上,对象的继承是从父对象继承特征的,因此他与兄弟姐妹共享这些特征。 参见示例: 问题是要知道使用哪个对象“孙子”来实现,但是这个主题很广泛,在这里可以得到最好的解释: 多重继承 多重继承是某些面向对象的计算机编程语言的功能,其中对象或类… en.wikipedia.org 协议是多重继承吗? 不,对不起。 对不起, 协议(或接口)实际上是多重继承的致命解决方案,因为它定义了对象的行为,但是实现仅在类中进行。 使用协议的简单事实不会创建一个合成环境。 但这使我们更加接近多重继承环境。 但是组成是解决方案吗? 当一个类具有另一个类的属性时,就是合成。 就是这么简单,我们每天都在使用这个概念。 因此,关于它的讨论是,我们始终具有明确的定义并限制了OOP概念的组成。此链接清楚地表明: 对象组成 在计算机科学中,对象组合(不要与功能组合相混淆)是一种将简单的组合… en.wikipedia.org Swift中的面向协议(POP)? 移动世界对新概念感到疯狂,所有这些都将使我们通过Swift。 每天我看到人们发布用语言创建的东西。 这是不可思议的,我认为极限是我们的想象力。 但是想象一下,如果不使用属性就可以组成行为,那么行为就构成了对象。 这正是我们通过扩展协议所获得的,这是一种构建灵活的面向对象结构的方法。 关于协议+扩展❤的示例 扩展协议是一种创建合成的方法,在这些情况下无需使用继承。 这足以改变Swift中继承的整个概念,这样我们就可以在不更改可可对象树的情况下插入行为。 在此示例中,很明显,我们的ViewController我的孩子仍然是UIViewController对象。 但是通过协议,我们可以输入默认行为,并可以创建将协议作为继承来接收协议的函数。 通过一定的复杂性,我们可以具有组合的优点,而不会冒产生多重继承复杂性问题的风险。 结论 本文的目的是提醒我们所有人,我们应该始终研究和质疑新技术及其对我们的允许。 我们必须讨论OOP的基本概念,因为它们每天都是软件开发的基础。 功能语言和反应式编程概念正在采用我们的软件,这是我们赖以生存的时刻。 但是最基本的概念变得更加重要,建筑物的基础是最重要的部分。 一篇非常好的文章,讨论了方法和如何帮助选择: 组成与继承:如何选择? 从一开始…………没有继承,也没有组成,只有代码。 而且代码繁琐,重复… www.thoughtworks.com 谢谢,我们将讨论任何事情。 争论是学习。 🙂
当我们与合作伙伴一起获得Apple Watch支持时,我认为我会分享一些有关Apple Watch开发的技巧。 它是如何工作的? 将WatchKit应用程序目标添加到现有项目时,Xcode会创建2个目录:WatchKit扩展名和WatchKit应用程序。 WatchKit扩展程序可以在iPhone上运行,但可以在与iOS应用程序不同的沙箱中运行。 WatchKit应用程序仅包含情节提要和资源。 数据通过蓝牙低功耗在WatchKit应用程序和WatchKit扩展之间进行同步,因此,避免在这两个目标之间发送大量数据是一个好习惯。 如何同步您的数据? 在WatchKit扩展程序和运行在iPhone上的iOS应用之间可以通过多种方式发送数据。 在尝试了NSKeyedUnarchiver和NSUserDefaults之后,我遇到了一个很好的开源库,名为MMWormhole,由MutualMobile的人员开发。 它基本上使用NSKeyedArchiver将数据写入文件,然后将文件写入应用程序的共享App Group。 它还使您可以将对象注册为侦听器,以便在传递新消息时得到通知。 让我们尝试一下吧! 我创建了一个名为Contacts的简单应用程序,该应用程序可让您在iPhone上浏览,添加和显示联系人的详细信息。 每次用户进入联系人详细信息视图时,相应的联系人详细信息都会显示在AppleWatch上。 考虑到您必须是Apple Developer程序的成员才能运行和测试Xcode中的演示项目。 这是有关如何配置您的应用程序以支持应用程序组的简短指南。 因此,我们只想发送Contact对象。 联系人模型如下所示: 由于MMWormhole使用NSKeyedArchiver编码对象,使用NSKeyedUnarchiver解码, 我们的Contact类必须符合NSCoding协议,因此我们需要重写以下两种方法: 同样,我们需要指定Contact对象用来编码或解码其实例变量的唯一键。 为此,我们需要在尝试通过蠕虫孔发送消息之前放入以下代码行 在我们尝试从虫洞读取消息之前 否则,我们将收到以下错误: 就是这样。 现在,我们准备使用MMWormhole传递自定义类对象。 我创建了一个WatchKitDataManager类,负责在iOS应用和WatchKit扩展之间发送和读取消息。 就这么简单! MMWormhole库的另一个重要功能是能够将对象注册为新消息的侦听器,因此我们可以非常轻松地更新UI。 这样做的方法如下: 现在,当我们同时运行我们的iOS和WatchKit应用程序时,我们可以看到,每次用户在相应的iOS应用程序上输入联系人详细信息视图时,Apple Watch上的联系人详细信息都会更新。 如果您有任何疑问,请随时通过mateusz.nadolski@tooploox与我联系。 您可以在此处在Github上查看源代码。
使用UITableView可以轻松集成UISearchController,而使用UICollectionView则不容易 问题 苹果公司的iOS类UITableView与UISearchController及其UISearchBar配合得很好,但是,几乎所有开发人员在尝试将搜索栏与UICollectionView对象进行可视化集成时都举手示意。 在开发负责显示大型组织的REST服务的照片缩略图的应用程序时,我发现了这个问题,并且我希望拥有与UITableView相同的布局。 基于StackOverflow和其他发布内容,大多数人要么不理会试图直观地集成类,要么重载了节标题。 解决方案 由于我希望搜索栏以动画方式显示和隐藏,并在UICollectionViewController的上下文中与相关的集合视图保持正确的尺寸,因此这比我期望支持iOS 9的工作量要多得多和10,但我确实为我的需要制定了解决方案。 为了实现这一点,我编写了一个名为DSSSearchController的UISearchController子类,该子类添加了许多用于显示和隐藏搜索栏的方法。 我将其设计为可与任何视图一起使用,但仅使用特定的集合视图对其进行了测试。 在下面,您将找到Objective-C标头和源代码以及Swift中的版本。 如您所见,需要大量代码来确保搜索栏相对于集合视图的大小正确。 在iOS 9和10中,Apple似乎不将AutoLayout与由UICollectionViewController管理的集合视图一起使用,因此这就是为什么使用框架进行大量计算的原因-试图使用AutoLayout被证明是有问题的。 早期,我发现某些动画会产生一些我不喜欢的搜索栏重影或覆盖,因此这就是为什么某些操作按其原样进行排序的原因。 为了使用我的类,我编写了一个名为configureSearchController的方法,该方法在viewDidLoad期间调用以初始化该类并在搜索栏上设置一些选项。 在我的原始代码中,获得searchBar属性后,将其barTintColor更改为深海蓝色,将tintColor更改为白色,并将其搜索字段的tintColor更改为深海蓝色。 另外,我的导航栏的barStyle属性具有UIBarStyleBlack。 当用户点击导航栏中的放大镜图标时,将调用toggleSearchBar:方法。 我发现当我要展示一个视图控制器时,我需要知道我的搜索控制器是否是要展示的控制器,这是在搜索字段具有焦点时发生的。 我确定我的解决方案可以改进,并且可能会在更高版本的iOS中中断,但目前可以使用。 希望iOS 10的后继版本将内置一个解决方案。
我可以肯定,我们许多用Swift进行编码的人经常会看到两个我们熟悉的weak且unowned关键字。 今天,我想讨论两个关键字之间的区别以及如何在应用程序中实现它。 为了讨论weak和unowned ,我们需要首先了解ARC(自动引用计数)。 ARC用于跟踪和管理应用程序的内存使用情况。 每当我们创建类的实例时,ARC都会占用大量内存,以存储有关其类型,属性和值的信息。 当实例不再使用时,ARC释放内存,并允许将该内存用于其他目的。 被释放的所有实例将不再有权访问其属性或方法。 每次创建或引用实例时,ARC都会增加一。 在Swift中,默认情况下,所有实例均创建为strong引用,除非我们将其声明为weak或unowned 。 通过将实例声明为weak实例或unowned实例实例,我们可以将其释放。 我们之所以能够取消分配实例的原因,是因为强大的引用周期可能导致内存泄漏。 餐厅类{ var客户:客户? } 客户类别{ var餐厅:餐厅? } 在这里,我们有一个Restaurant实例,它具有一个Customer实例作为其属性。 如果我们要创建任何一个类的实例,并想取消分配,则由于强大的引用周期,我们将无法这样做。 这两个实例相互之间具有很强的参照性,从而使ARC保持不变。 弱 餐厅类{ 弱var客户:客户? } 客户类别{ var餐厅:餐厅? } 强引用循环的一种解决方案是将属性声明为weak 。 通过使用weak引用,ARC在释放引用时会自动将其设置为nil。 以我们的餐厅和客户类别为例,这一次,我们将餐厅类别中的客户属性设置为弱变量。 如果我们要将客户属性设置为nil,它将成功取消分配并中断强引用周期。 拥有的 餐厅类{ 无主var成分:成分 var客户:客户? } 类成分{ var餐厅:餐厅? } owned参照与weak参照非常相似,但是owned参照必须具有值。 我们不能将自己的引用设置为nil。 提示是,如果知道您的价值在生命周期的某个时刻将为零,则使用weak引用;如果您确定存在价值,则使用owned 。 让我们看一下上面的例子。 在这里,我们有一个餐厅和食材类,餐厅类具有无主成分。 我们知道餐厅里一定有食材,但是我们可能有也可能没有顾客,因此在这种情况下我们必须使用unowned。
如果您正在阅读本文,那么您很可能已经知道使用泛型的好处。 您想要减少编码。 您希望编写的代码能做更多。 您以前曾尝试在编程中使用泛型,但有可能使您从未想到的挫败感达到顶点。 你不是一个人。 能够以一种通用的方式工作,但是仍然可以成功地推断出您的类型可能很棘手。 幸运的是,将泛型与协议的使用结合起来确实很有效,而且一旦您学习了少量技术,您就会发现这棘手的事情实际上并不是那么棘手。 为了探讨该主题,我们将构建和完善一个针对以下四个方面的规范设计模式: (我希望)这些技术将成为您的编码库中的主要内容。 顺便说一句,这种模式是解决开放-封闭原则的极好方法。 我强烈建议您熟悉的东西,以及我最近 在这里 写的一些东西 。 过滤产品 我们的假设情况是,我们有一个称为Product的实体,并且我们希望能够按某种规格过滤一系列产品。 首先让我们看一下Product : 这是有道理的,因为我们的协议类型Specification是泛型的,而且我们可以可行地添加一个规范实例,该实例的关联类型不是Product 。 我们需要做的是检查items参数中的类型是否与spec参数中的类型相同。 让我们尝试一下。 技术2:检查通用实例上的条件 我们可以使用以下模式在函数声明的开头执行检查: func someFunc (参数:T)其中T.someProperty == self.someProperty 请注意,我们在输入参数之前使用了典型的占位符语法 ,以表明我们的函数正在使用泛型类型。 为了能够访问T实例的内部,我们需要首先说一下实例的类型是 ; 在这种情况下, T的类型为SomeType 。 接下来,我们需要表明我们要检查使用where关键字完成的条件。 最后,我们可以访问T.someProperty类型的属性并执行布尔检查。 现在,只要通用类型为SomeType且其值someProperty与我们自己的类型相匹配,该函数就可以使用。 考虑到这一点,让我们构建一个通用过滤器 ,该过滤器可以在规范associatedtype与物料类型匹配的情况下接受任何物料和任何规格。 现在,代替将specs参数类型设置为Specification ,我们可以改用Spec,因为它具有与我们Filter (第5行) 中的那个相关联的关联类型 ,这是安全的。 创建协议后,实现Filter本身非常简单。 我们遵循协议(第9行),设置类型别名 (第10行),然后filter(items:,specs 🙂神奇地将其自身设置为正确的类型。 现在,我们可以使用我们先前制作的Product实例进行测试 在这一点上,我们可以肯定地说一切正常。 使用我们当前的设计,创建更多的过滤器和规格是一项非常简单的任务。 但是,让我们看看是否可以进一步完善它。 目前,我们的ColorSpecification实体仅适用于Product类型的实例。 […]
欧内斯特·阿什·什迪奇(Ernest Ash Shiddiqi)(美国从YP 16-17获纳伊斯) 每个交换生都同意面对交换年的恐惧之一就是环境如何接受他们。 这也是我最大的担忧之一。 害怕被隔离,没人愿意和我成为朋友,这些事情令人沮丧,尤其是在第一个月。 试想一下,独自一人呆在一个全新的地方,没有我们认识的人,我称之为噩梦。 幸运的是,那场噩梦并没有变成现实。 我很高兴我的家庭如此开放,能够接受所有分歧。 除了家庭,我还有另一个“家庭”。 他们是在绿色领域战斗的人。 他们被称为迦太基大学足球队。 我参加了夏季的大学足球计划。 在那个程序中,我发现了一些非常重要的东西。 不,不是踢足球的技能,而是朋友。 团队几乎和家人一样重要。 他们是我在美国认识的第一批人。 我们在一起经历了很多事情。 从快乐到悲伤,从失败到……我们甚至从未获得过真正的胜利。 与这里的其他程序相比,我们是某种失败者。 但是,即使我们还没有赢得任何比赛,我仍然为成为我们称为“ Fam”的团队的一员而感到自豪。 到目前为止,第一个月是我一生中最糟糕的事情。 现在是想家的时候了。想念我的父母,兄弟,家,食物,基本上所有的东西。 我试图用我能想到的所有方法来应对它。 但是我意识到,不仅仅是我的努力使我成功,这里还有一些X因素。 这个因素来自一个叫做孟加拉国的国家。 我和另一个来自孟加拉国的YES学生一起就读。 他的名字叫塔拉特·哈桑。 朋友,最好的伙伴和兄弟在同一时间。 他是使我在最初的几个月中不断挣扎的人。 我们为了生存而战斗。 那就是您所说的动态二重奏。 我们互相帮助,互相支持,而且由于命运相同,我们俩团结了起来。 我不仅在这里经历了与另一个人的联系。 而且还有与自然的联系。 这可能是我最大的文化冲击经历之一。 与雅加达相比,这里一切都颠倒了。 雅加达从不睡觉,而迦太基则一直在睡觉。 当它在雅加达达到零度以下时,这是奇迹,而在这里,这只是日常生活。 雅加达充满了混乱和忙碌,而迦太基充满了平静与安宁。 但是,我非常感激所有这些,因为这就是您所说的交换。 因为作为交换生的真正本质是尝试一些舒适的地方。
通知中心是一种内部沟通工具; 它为类/结构的一个实例提供了一种方法来通知一个或多个其他实例。 通知中心充当通知的“广播者”。 将通知发布到通知中心时,可以使用唯一键进行标识。 在等待接收通知的类/结构实例中,您设置了一个“观察者”,它具有与发布的通知中相同的唯一键。 创建观察者时,您还指定观察者从通知中心收到消息后将执行的功能 在下面的代码中,有两个通知被发布:“ login-success”和“ animate-label”。单击该按钮时,将发布“ login-success”通知(在sendNotification()中)。 viewDidLoad中的观察者可确保VC知道它正在“观察”某物。 发布“成功登录”通知后,观察者将收到该通知并执行编写在#selector()中的函数。 因此,在下面的示例中,它将运行notificationSegue()。 在notificationSegue()中,发布了第二个通知(“ animate-label”)。 notificationSegue()锁定到的VC称为ViewController(粘贴在下面)。 如您所见,该VC也具有观察者,但是它没有观察“登录成功”,而是在等待接收带有唯一键“ animate-login”的通知。由于notificationSegue在此观察的那一刻发布了该观察者。要执行此VC,观察者将收到通知并触发animateHelloLabel()。 关于NotifcationCenter需要注意的一件事是,当观察者不再需要侦听通知时,必须将其删除。 希望这对您有所帮助,并且祝您编程愉快!!
最近,我一直在为iOS应用程序实现缓存机制。 为了实现这一目标,我设定了以下目标: 利用Codable协议轻松解析来自Web服务的JSON响应并创建适当的模型实例。 将所需的模型实例存储在Core Data中。 这项任务是一次有趣的学习经历。 因此,我决定回到我的示例应用程序之一,以说明如何使数据模型支持Codable并与Core Data Codable工作。 模型:NSManagedObject和Codable 我开始的示例应用程序只有一个简单的模型User ,如下所示: Core Data的用例非常简单:允许离线使用该应用程序(即:当网络连接不可用时)。 实现此目的的最简单方法是,每当应用程序具有网络连接时,就删除并重新创建Core Data数据库。 这就是为什么在parse(…)方法中,在实际解析JSON响应之前调用clearStorage() :在开始添加已解析的User实例之前,我们想清除存储(数据库)。 清除存储所需的代码非常简单,因为我们只需要删除一个表即可: 为了检索存储的User实例, UserController提供了fetchFromStorage()方法: 两种方法都通过NSFetchRequest执行各自的任务。 通过实现上述两种方法,我们现在拥有了与Core Data和User实例成功交互所需的一切。 结论 在这篇文章中,我描述了我使用Codable和Core Data的个人经历。 特别是,我专注于如何无缝解析JSON响应并将结果模型存储在Core Data中的适当数据库表中。 这篇文章中说明的示例应用程序的代码可在GitHub上找到。
正如我在先前有关操作扩展的博客中提到的那样,我正在创建默认Apple Notes应用程序的副本,其中还包括创建共享扩展名,该共享扩展允许我的Notes应用程序接受其他应用程序中的URL和文本。 这里的主要挑战是,我希望我的共享扩展名能够创建或添加到保存在CoreData中的现有笔记中。 整个项目可以位于此处(免责声明,我仍在对其进行调整): almusto / CustomNotes 通过在GitHub上创建一个帐户为CustomNotes开发做出贡献。 github.com 我做了很多的在线查找工作,但从未真正找到一个很好的例子说明我正在尝试做的事情。 其中一些方法包括在CoreDataStack的init中使用NotificationCenter并使用它来传递数据的方法,但是我发现了一种更简单且似乎很好的方法! 就像所有其他扩展程序一样,Share Extensions基本上是它们自己的应用程序,但是它们需要作为项目的一部分而不是独立的项目包含在内。 要在项目中创建共享扩展名,请转到文件->新建->目标,然后选择共享扩展名。 创建后将出现一个弹出窗口,询问您是否要使其激活。 这意味着在构建应用程序时,它将构建扩展而不是应用程序。 您可以选择否或是,然后手动更改要在Xcode左上方运行的内容: 如前所述,我在Notes应用程序中进行了股票扩展。 为了使这两个应用程序彼此通信,首先要做的是将您的应用程序和扩展名连接到同一应用程序组。 为此,请转到项目功能,然后将您的应用程序和扩展程序的“应用程序组”都打开,然后将它们添加到同一组中: 在共享扩展本身中,您会注意到它带有类型为SLComposeServiceViewController的ShareViewController,其中具有三个函数: isContentValid()->布尔 didSelectPost() configurationItems()-> [任何]! 为了解释这些功能,您可以使用Safari快速运行扩展,然后单击活动按钮时,应该看到扩展已加载,如下所示: 在不编写任何代码的情况下单击它之后,您将看到此消息: 如您所见,它就像是Safari中自己的小应用程序。 回到您可能已经猜到的三个函数,当您单击发布按钮时,didSelectPost()被触发。 isContentValid()实际上是在检查我们尝试与应用程序共享的内容是否为正确的类型。 此功能确定是否启用发布按钮。 最后,configurationItems()函数使我们能够将信息发布到特定对象。 在此示例中,我希望能够通过Core Data在CustomNotes应用程序中创建或添加到现有笔记中。 在开始在扩展程序中编写代码之前,让我们回到我的customNotes应用程序以提供一些上下文,并开始与扩展程序共享coreData的过程。 Core Data上还有其他资源,因此除了与共享扩展有关的需求之外,我将不做过多介绍。 我个人喜欢创建一个可以处理我所有核心数据需求的核心数据栈。 在此示例中,我的数据模型称为“ NotesModel”,它具有“ Note”类型的实体和两个属性。 一个叫做String的“ title”,另一个叫做Date的“ date”,在CoreData中实际上是NSDate类型。 以下是我的CoreDataStack: 现在,在此示例中,我只想允许我的notes应用程序接受URL和文本。 第一步是进入扩展程序的plist文件,并将NSExtensionActivationRule更改为字典,然后将NSExtensionActivationSupportsWebPageWithMaxCount作为数字添加为1,将NSExtensionActivationSupportsText作为布尔值添加为YES。 同样,当我们在这里时,您可以将Bundle显示名称更改为所需的名称,因为此名称是设置活动控制器时实际显示的名称。 现在,在我们的ShareViewController中,我们创建一些变量来保存数据。 一方面,我希望变量保留我的网址信息,在我的示例中,我只希望该网址为字符串。 另外,如果我只是想共享文本,我希望有一个变量可以保留该文本。 一定要在底部启用这个小的选择注释选项,我们需要使用我们的configurationItem函数,但是我们想用CoreData中的注释填充它,因此让我们向ShareViewController添加一些属性。 一个用来存储我们所有的笔记,一个用来创建我们的CoreDataStack实例,另一个用来保存我们选择要发布到的笔记的值。 var selected注意:注意! […]
在第一部分中,我们介绍了串行和并发队列以及示例。在这一部分中,我们将详细介绍有关具有参数更改的并发队列的苹果,并学习如何创建全局队列,非活动队列,时间延迟,死锁。 让我们从我们离开的地方开始。 InactiveQueue: 在attribute参数中有另一个值initialInactive。因此,我们可以首先使非活动队列成为队列,当需要活动时,我们可以使它处于活动状态。 让我们将属性值并发更改为initialInactive,如下所示: let newQueue = DispatchQueue(标签:“ com.concurrent.ekram”,qos:DispatchQoS.background,属性:.initiallyInactive) 并在并发函数的外部块中创建DispatchQueue的类属性: var inactiveQueue:DispatchQueue! 现在在这样的函数中使用newQueue初始化不活动队列: inactiveQueue = newQueue 现在,newQueue处于非活动状态,并且viewDidAppear不知道队列,因此需要在viewDidAppear中手动激活,如下所示: 如果让队列= inactiveQueue { queue.activate() } 现在,代码将以串行方式运行并执行队列。请参见屏幕和控制台: 最初不活动时如何使用并发: 属性参数很简单,请同时使用.initiallyInactive和的数组。 并发比队列最初也将处于非活动状态并发。 let newQueue = DispatchQueue(标签:“ com.concurrent.ekram”,qos:DispatchQoS.background,属性:[。initiallyInactive,.concurrent]) 现在的结果是: 因此,当我们使用active()方法在viewDidApper上同时运行时,newQueue最初也处于非活动状态并且是并发的。 创建全局队列: 我们已经知道如何使用property创建自定义队列。 实际上,我们不需要创建全局队列,因为GCD工程师已经为我们创建了全局队列。因此,我们可以像这样创建.. 让globalQueue = DispatchQueue.global() 我们可以添加到全局队列方法 globalQueue.async { 对于i in 0 .. <10 { 打印(“ Custom Green Love:Custom”,i) } } […]