使用像Quick这样的spec测试框架很不错,它可以启用BDD样式。 describe(““文档”目录”){ 它(“拥有开始所需的一切”){ 让节= Directory(“文档”).sections Expect(section).to(contain(“包含快速示例和示例组的组织测试”)) Expect(section).to(contain(“快速安装”)) } context(“如果没有您要的东西,”){ 它(“需要更新”){ 让你=你(真棒:真) 期望{you.submittedAnIssue}。最终到达(beTruthy()) } } } 但是,如果您不想使用其他框架,并且希望尽可能地靠近Apple SDK,可以参考以下提示。 这是我非常喜欢“单元测试的艺术”的书。 如果您不介意下划线,则可以遵循UnitOfWork_StateUnderTest_ExpectedBehavior结构 func testSum_NegativeNumberAs1stParam_ExceptionThrown() func testSum_NegativeNumberAs2ndParam_ExceptionThrown() func testSum_simpleValues_Calculated() 这是来自BDD的,并且在Cucumber中实践了很多。 您可以在martinfowler.com/bliki/GivenWhenThen.html上了解更多信息。 首先,向XCTestCase添加更多扩展 导入XCTestextension XCTestCase { 给定的func(_说明:字符串,闭包:()引发->无效)引发{ 尝试闭包() } func when(_描述:字符串,闭包:()throws-> Void)throws { 尝试闭包() } func then(_描述:字符串,闭包:()throws-> Void)throws { 尝试闭包() } } 然后,为了进行测试,只需遵循given when then func testRemoveObject()引发{ 尝试给定(“设置为存储”){ 尝试storage.setObject(testObject,forKey:key) }试试when(“从存储中删除对象”){ […]
通过场景之间的动画和/或交互式过渡来制作引人入胜的精美UX。 当视图控制器过渡到另一个视图控制器时, 实际的过渡操作以通过UIViewControllerContextTransitioning类型的对象访问的过渡上下文的形式在容器视图中进行 ,您可以在其中访问过渡器的两个视图(“ fromView”)和过渡对象(“ toView”)。 我们将看到如何在视图控制器之间建立动画过渡,并且默认情况下将它们准备好是交互式的,因为如果需要, UIPropertyViewAnimator允许我们与旧的API相当平稳地执行此操作。 更好地了解如何实现“完整的事情”,并且如果需要的话,可以使过渡成为非交互式的要好得多。 别偷懒,开销只是几行代码。
我最近写了一篇有关iOS如何在显示允许用户登录或注册的Web视图之前如何检测连接到新Wi-Fi网络的强制门户的文章。 对于大多数在酒店,酒吧或咖啡厅等处连接到公共Wi-Fi网络的人来说,这种情况很熟悉。如果您还不了解其工作原理,请 在iOS上解决强制门户问题 提供了这篇文章的有用背景。 多年来,Apple的Reachability示例代码一直被用作检测第三方iOS应用程序中网络访问的实际起点。 在Cocoapods.org上进行的快速搜索将显示一长串库,这些库重写了此代码时考虑了许多因素,例如ARC支持或Swift兼容性。 WWDC在2018年6月推出了从iOS 12起可用的Network框架,其中包括NWPathMonitor类。 此类为我们提供了一种监控网络状态变化的方法,而不必包含第三方库/ Apple示例代码。 为了利用NWPathMonitor类,只需导入Network框架,然后创建一个NWPathMonitor实例: 让监视器= NWPathMonitor() 如果仅对特定网络适配器(例如Wi-Fi)中的状态更改感兴趣,则可以使用init(requiredInterfaceType:)初始化程序并提供NWInterface.InterfaceType作为实例来指定要监视实例化NWPathMonitor对象的网络适配器。参数例如 让监视器= NWPathMonitor(requiredInterfaceType:.wifi) 您需要确保在某个地方保留对该对象的引用(例如使用强属性),否则您可能会发现,当ARC释放NWPathMonitor对象时,分配给您的回调将停止调用。 可以监视的接口类型包括: cellular loopback other (针对虚拟或不确定网络类型) wifi wiredEthernet 要通知状态更改,您需要为pathUpdateHandler属性分配一个回调,只要网络接口中发生状态更改(例如,您的电话从蜂窝网络移动到Wi-Fi网络),就会调用该回调。 然后,无论何时发生状态更改,都会返回一个NWPath实例,可以查询该实例以确定我们是否已连接,如下所示: monitor.pathUpdateHandler = { 如果path.status == .satisfied { 打印(“已连接”) } } 使用无参数初始化程序与使用指定网络适配器的初始化程序会影响是否satisfied.返回的NWPath对象的status属性satisfied. 例如,如果您选择监视蜂窝网络适配器,但是Wi-Fi适配器发生状态更改(例如,您的电话连接到Wi-Fi网络),则您的回调将不会被调用,并且路径的状态将unsatisfied (可以使用currentPath属性随时访问NWPathMonitor的路径),因为未使用指定的接口连接设备。 因此,如果您只是想知道是否存在连接,无论是Wi-Fi还是蜂窝网络,那么最好坚持使用无参数初始化器。 有趣的是-虽然NWPath对象是iOS 12中作为Network框架的一部分而新增的,但实际上,自iOS 9起, NWPath对象已作为NWPath一部分使用(有一些细微差别)。 可以查询返回的NWPath对象,以了解有关设备网络适配器状态的大量信息。 更有趣的属性之一是isExpensive ,它返回是否认为网络接口使用昂贵(例如,蜂窝数据计划)昂贵。 我们还可以找出该路径是否支持DNS,IPv4或IPv6。 如果我们需要找出哪个接口更改了状态并触发了回调,则可以调用usesInterfaceType方法: let isCellular:Bool = path.usesInterfaceType(.cellular) 使用NWPathMonitor与使用其他iOS API(例如CLLocationManager […]
如果您有一个应用程序正在监视一个来自MQTT代理的实时数据流,如我在先前的文章中所述,则您可能希望基于该实时数据流发送用户通知。 这可以在多种情况下工作,但就我而言,我有一个传感器,如果传感器检测到某个读数,则想警告用户。 为此,我遇到了两种可能性: 一种。 设置APNs(Apple推送通知服务)服务器,该服务器监视MQTT数据并在必要时向每个设备发送推送通知 要么 b。 直接从客户端设备读取MQTT数据,并在必要时发送本地通知 在我的情况下, 选项B更具吸引力,因为它消除了中间人 (APN),这意味着您不必设置服务器即可执行该任务。 选项B不仅更容易,而且还节省了大量的时间和运行APN的设备资源。 但是,您应该注意,有些付费服务可以为您处理APN。 如果您的项目规模较大且预算较大,则可能需要进一步研究该选项。 为了建立这个MQTT后台通知系统,我们需要弄清楚一些事情: 设置背景提取 在后台获取过程中获取mqtt数据 必要时发送通知 它是什么 在iOS中,后台抓取是指将应用从后台唤醒以执行简短的任务,这通常涉及检查服务器上是否有要下载的新信息。 这很有用,这样用户回去打开应用程序时就可以为他们准备好新信息,而不必等待其加载。 如果您愿意,可以在这里阅读更多有关后台获取和其他后台模式的信息。 如何设定 为了在您的应用程序中实现后台提取,您必须将您的应用程序标记为具有该功能。 为此,请选择应用程序目标 ,然后在顶部选择“ Capabilities ”。 从那里打开背景模式开关,并选中背景提取框,如下所示: 现在,您必须告诉应用程序您要多久运行一次后台获取。 为此,将以下内容添加到application(didFinishLaunchingWithOptions:)方法中: //尽可能频繁地运行后台抓取 UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum) 开发人员可以根据具体情况决定此频率,但就我而言,我绝对希望尽可能频繁地获取数据。 现在,您可以在AppDelegate中添加一个方法,该方法将在每次后台获取时调用。 它看起来应该像这样: func应用程序(_应用程序:UIApplication,performFetchWithCompletionHandler completeHandler:@转义(UIBackgroundFetchResult)->无效){completionHandler(UIBackgroundFetchResult.newData) } 当然,我们稍后会添加到该方法,但是正如您已经看到的那样,该方法必须在完成时调用完成处理程序 。 完成处理程序报告是否找到了新数据,系统将使用它来确定运行应用程序的后台获取的最佳时间以及应多久运行一次。 因此,至关重要的是准确地报告该信息,而不是像上述方法那样盲目地说有新数据。 该操作只是作为占位符完成的,稍后我们将修复该问题。 现在,我们要在后台获取期间接收我们的MQTT数据。 设置MQTT可以是其自己的一个微型项目,因此,如果尚未设置MQTT,则应阅读此文章。 当然,在这种情况下,我们将需要做一些更改以使MQTT有用。 我们将需要从后台模式创建一个连接,并使AppDelegate成为MQTT连接的委托。 我们可以使用与上述文章到AppDelegate相同的方法来建立连接: func setUpMQTT(){ 让clientID =“ CocoaMQTT-” +字符串(ProcessInfo()。processIdentifier) […]
自从我撰写有关该主题的第一篇文章以来已经过去了两个月。 那篇文章介绍了Xcode的构建时间分析器,该分析器旨在帮助确定Swift编译器难以解决的地方。 从那时起,该插件就遍及整个Swift社区,并且在WWDC实验室期间,看到了很多人在使用它。 因此,发生了一些非常有趣的事情。 Apple与我联系并提出了增强请求(请参阅新的“发生次数”列)。 可以通过插件识别导致构建时间缓慢的两种原因。 第一个是单个例程花费太长时间来编译的地方。 这是我以前的文章的重点,我在那里列出了一些示例和解决方法。 另一个是对闭包和惰性属性进行类型检查的次数过多。 这将是这篇文章的重点。 闭包和惰性属性 我最近在插件窗口中添加了一个名为Occurrences的新列。 目的是使人们更容易理解为什么相对简单的代码有时会减慢构建过程。 如上所示,在我的Xcode构建日志中,有几次惰性获取器发生了159次。 实际上,我可以在日志中打开任何文件并查看对其的引用。 下面是从3个单独的文件中摘录的一些示例,这些文件均未在代码中引用CMGridView。 5.7ms /CMGridView.swift:63:27 @objc get {} 16.5ms /CMGridView.swift:63:27 @objc get {} 15.5ms /CMGridView.swift:63:27 @objc get {} 因此,事实证明编译器正在为目标中的每个.swift文件类型检查我的lazy属性,从而使累积生成时间总计为1905.5ms(使用Swift 2.2)。 对于Swift 3.0,问题仍然存在,但是构建时间几乎减少了一半。 如果您在项目中使用的是惰性属性,我真的建议您注意这一点。 在上面的示例中重构代码之前,我曾经有很多这样的代码。 让我们看一下代码。 代码: 解决方法 为了缩短这些代码的构建时间,只需将代码尽可能移至私有方法即可。 上面的lazy属性仍将接受重复的类型检查,但是随着代码的移出,构建时间现在减少了96.7% 。 到目前为止,在Swift 3.0中的构建时间 随着Xcode 8.0的出现,Xcode插件的时代结束了,Xcode扩展的新时代开始了。 鉴于扩展的局限性,我正在努力使该插件成为一个独立的应用程序。 (希望)在Xcode 8.0脱离beta版本之前就可以了。 同时,这是根据我之前的文章中列出的示例进行的一些比较。 三元运算符 无合并运算符 ArrayOfStuff + […]
创建应用程序从未如此简单,但是并不是每个开发人员都是图形或音频设计方面的杰出艺术家或专家。 因此,这就是您创建了一个出色的应用程序的问题,该应用程序可以完美运行,但是从外部看,它看起来并不尽如人意。 这给您提供了两个选择,要么聘请图形设计师来专门为您的应用程序设计资产,要么使用在创用CC许可证cc0下发布的资产。 根据此许可发布的任何内容均被视为无版权,这意味着您可以以任何喜欢的方式使用它。 您可以为您的应用程序使用cc0声音,甚至可以发布和出售具有根据此许可证发布的资产的应用程序。 但是,通常情况是免费的时候,要找到免费的又高质量的东西并非易事。 这就是为什么我们在RUME ACADEMY希望共享一些指向具有功能强大的应用程序资源的资源的链接,而无需任何费用。 让我们从告诉您在哪里获得免费游戏资产开始,因为游戏通常是需要大量资产才能表现出色的应用程序。 图形资产: 到目前为止,图形资产的最佳资源是KENNEY.NL Kenney完全免费提供数百种令人惊叹的专业外观游戏资产。 您当然可以捐赠一些东西,在我看来,这是绝对值得的,因为他们的所有资产都令人难以置信。 无论您需要2D,3D还是仅某些纹理,都可以在其网站上找到所有这些。 OpenGameArt.org 游戏资产的另一个重要来源是OpenGameArt.org OpenGameArt可能不像Kenney那样吸引人,但是它们确实有很多可以使用的资源。 这里要记住的事情是,您需要确保使用的是根据正确许可发布的资产。 即使有很多资产是cc0,但其中一些资产仍在“归因许可”下列出,这意味着在使用某些作品时,您必须将其归因于创作者。 Gameart2d.com 尽管网站上的大多数资产都在出售,但它们的确有一个令人赞叹的免费赠品部分,您可以在其中获得许多令人惊叹的精灵和游戏GUI。 其余资产待售,但有免版税的权利,这是您在购买资产时必须牢记的,因为您不想支付专利费,以防您的游戏真正冒了顶。 图片资产: 如果您要开发一个更复杂的应用程序,并且正在寻找免费版权图像资产的重要来源,则应转到 Pixabay 您可以免费注册并在应用程序或网站上使用超过700000张专业拍摄的照片,而无需支付任何费用。 音频资产 现在我们已经拥有了所需的所有图形,现在该看看可以使用的音频资源了。 Freesound.org 网站freesound.org是最简单,最快的获取免费声音的方法。 您必须创建一个帐户才能下载任何内容,但它是完全免费的,并且通常不会因您不想要的电子邮件或其他内容而烦扰您。 创建帐户后,您可以在网站上下载任何声音。 有不同的许可证,但是每个文件都指定了许可证,因此您可以确切知道是否可以将这种特定的声音用于商业用途,而且您是否应该相信创作者。 但是,实际上,您可以在任何项目中使用大量声音文件,而无需担心这太棒了! 在这里找到正确的声音可能要花费一些时间,因为文件的命名通常很差,但是您可以在这里免费找到真正的美妙声音。 Audiojungle.net 另一种选择是audiojungle.net。 与用于图形的Graphicriver一样,该网站也是Envato网络的一部分。 您会在这里找到的声音确实要花钱,但是通常1美元起就可以负担得起。 您可以期望高质量的回报,从而使您的声音真正脱颖而出。 声音是经过分类的,因此您可以在正确的类别中方便地进行搜索,以准确找到所需的内容。 您将需要创建一个Envato帐户,这是一件好事–不仅对于Audiojungle,而且对于网络上所有其他出色的网站。 既然您知道了从哪里获得使应用程序专业外观和声音所需的所有资产,那么现在该学习如何专业地构建应用程序了。 我们决定在udemy上为您提供90%以上的iOS专业Swift开发课程。 只需在结帐时使用代码MEDIUM15。 或直接点击此链接
想象一下,您已经完成了应用程序的开发和测试,现在可以将其提交生产版本了。 但这是问题所在:您所有的API密钥,URL,图标或其他设置都针对测试环境进行了配置。 因此,在提交您的应用程序之前,您将必须修改所有这些内容以适合您的生产模式。 显然,这听起来不太好。 另外,您可能会忘记在庞大的应用程序中进行某些更改,因此您的服务将无法正常运行。 代替这种凌乱的方法,最好有几个环境并在需要时简单地更改它们。 今天,我们将通过最流行的方法来尝试组织不同的环境: 使用注释。 使用全局变量或枚举。 使用带有全局标志的配置和方案。 将配置和方案与多个* .plist文件一起使用。 1.使用注释 当您有2个分离的环境时,您的应用程序需要知道它应该连接到哪个环境。 想象一下,您拥有Production , Development和Staging环境以及API端点。 处理此问题的最快,最简单的方法是拥有3个不同的变量并注释其中2个: 这种方法非常肮脏,凌乱,会使您哭泣很多。 有时我会在黑客马拉松上使用它,因为代码的质量不起作用,而速度和灵活性才是关键。 在任何其他情况下,我强烈建议您完全不要使用它。 2.使用全局变量或枚举 另一种流行的方法是使用全局变量或Enum (这会更好)来处理不同的配置。 您将必须在3种环境中声明您的Enum ,并在某处(例如在AppDelegate文件中)设置其值: 这种方法要求您每次更改代码时都只在代码中设置一次。 与以前的方法相比,此方法要好得多。 它非常快速,可读性强,但有很多局限性。 首先,在运行任何环境时,您始终具有相同的捆绑ID。 这意味着您将无法同时在设备上拥有两个具有不同环境的相同应用程序。 一点都不舒服。 同样,在每个环境中使用不同的图标也是一个好主意,但是采用这种方法,您将无法更改图标。 同样,如果您忘记在发布应用程序之前更改此全局变量,则肯定会遇到问题。
Swagger Codegen是开源API客户端代码生成器,对于您的团队而言,它可以成为功能极其强大,节省时间的协作工具。 与大多数功能强大的工具一样,它可能无法立即满足您的需求。 为了让Swagger Codegen真正为您和您的团队工作,了解Swagger Codegen的工作方式将很有帮助。 但是,首先您可能会问,为什么? “我们鼓励您开发程序员的三大美德: 懒惰,不耐烦和傲慢。” — LarryWall,O’Reilly And Associates的ProgrammingPerl(第一版) 在讨论代码生成工具时,我会一开始就不提拉里·沃尔(Larry Wall)的“程序员的三种美德”。可以为懒惰辩护。 在这种情况下,懒惰由拉里·沃尔(Larry Wall)在其著名的《 编程Perl》一书的词汇表中解释为:“使您尽全力减少总体能源消耗的质量。 它使您可以编写省力的程序,其他人会发现它有用……” 现在,当拉里·沃尔(Larry Wall)赞扬程序员懒惰的优点时,他并不是指偷工减料和做懒惰的事情,例如不编写单元测试。 拉里·沃尔(Larry Wall)对懒惰的定义在任何情况下都不应视为对懒惰的辩护。 但是,如果您正确地做到了“懒惰”,并且希望您在阅读本文后会如此,那么使用Swagger Codegen不仅意味着减少自己的能源消耗,而且还意味着团队中所有成员的能源消耗减少。 您无需编写,维护或担心自动生成的代码的一致性! 您可以节省工作和精力,例如编写有关代码生成的博客。 😇 但是,等等,在我们开始凭空生成代码之前,我们必须谈论作为团队达到目标所需要的东西。 OpenAPI规范(以前称为Swagger规范)是“与RESTful API无关的语言的接口 ”,它使各种精通不同编程语言的开发人员可以以每个人都能理解的方式讨论REST API。 该规范允许开发人员创建合同,该合同定义API的工作方式以及应该在任何人编写一行代码之前执行的操作。 这样一来,将创建并维护API的开发人员及其客户就非常特定的合同达成协议,并说:“如果我将带有这些标头的正文发布到此端点,那么我期望以这种格式进行响应。” 举一个具体的例子,我邀请您查看此PetAPI应用程序的OpenAPI规范。 当您了解有关OpenAPI规范的更多信息时,Pet Store示例就是一个非常熟悉的示例。 这是每个开发人员为每种新语言实现代码生成规则时使用的参考点。 集成测试始终根据OpenAPI Initiative的Github Repo中的OpenAPI规范执行。 简要回顾一下`petstore.yaml`我们可以看到已经定义了一个简单但完整的API。 根据此规范中定义的要求,我们可以实现一个后端服务,该服务返回数据库中所有宠物的列表,或将新宠物写入数据库,然后发布到API。 在前端,我们可以构建与API交互所需的模型,并实现可用于调用端点以获取新宠物或发布新宠物的正确服务。 我们可以做所有的事情……但是作为“懒惰”程序员,我们可以使用Swagger Codegen代替它! 在使用Swagger Codegen之前,您需要在本地安装它。 有多种不同的方式来安装和使用Swagger Codegen。 为了最大程度地控制修改项目以适应我们的需求(并与该博客一起进行),获取Swagger Codegen的最佳方法是克隆整个存储库:https://github.com/ swagger-api / […]
HOF钻孔,高阶功能 哦是的!!! 您完全看到了。 钻孔:不是“打个洞”,而是军事版本的训练。 在这里… mobidevtalk.com 哦是的!!! 您完全看到了。 钻孔:不是“打个洞”,而是军事训练。 在这两个博客文章系列中,我们将深入探讨Higher order Function aka HOF 。 所以收紧靴子,会很泥泞。 如果您是可以使用HOF的人或女孩; 嗯,我猜😉。 但是您对幕后发生的事情很感兴趣,然后您就处于适当的位置。 我们将HOF演练分为两部分。 内容: 第一部分,意味着此博客文章,将详细介绍HOF。 在第二部分,我们将生成一个复杂的案例研究,我们将使用HOF进行解决。 这样一来,我们最终可以掌握HOF。 背景: 真正的HOF是什么! 为什么要使用 预先知识 Swift的一些HOF 你好,我是HOF 尽管HOF这个名称似乎很难理解,但实际上并非如此。 HOF唯一困难的事情是对何时使用哪种HOF的理解。 HOF是一个非常简单的概念。 HOF , Higher Order Function ,基本上是具有以下两个属性的函数: 带/不带其他参数的闭包作为参数 返回一个函数或一些累计值 简而言之, HOF将有助于重复 操作 。 现在,这两个关键字repetitive和operation意味着什么? 关键字repetitive ; 你已经猜对了吧? 是的,它仅对集合类型有效,例如Array Dictionary Set 。 集合的每个元素都将包括在内。 其次, […]
在开发应用程序时,我们可能会遇到这样的情况,即我们无法使用的数据结构真正适合我们要完成的任务。 最近,我遇到了这种情况,正在开发一项功能,该功能要求使用事件监听器的集合。 今天,我想我会借此机会分享设计自定义数据结构背后的思考过程,并提出一些在实现它之前可能要回答的问题。 步骤1:要求 每个设计过程的第一步都是实际确定您希望新结构能够完成的工作。 这包括写下绝对必须要做的事情,以及在哪些方面我们可以摆脱“次优”表现。 让我们针对我们的侦听器列表进行此操作。 在我们的列表有效期内,听众可能会分配,也可能不会分配。 我们不希望我们的列表成为对象保留在内存中的原因(并有可能以意想不到的方式操纵我们的程序),因此我们将需要一些保持弱引用的对象。 由于侦听器对象可能已释放,因此我们需要一种清除这些对象的方法。 清理结构最多需要O (n)时间,并且应该可以将清理作为其他操作的一部分进行。 使用侦听器列表时,将对象插入列表比删除对象更常见。 因此,插入将需要尽可能快,但是移除将不是关键。 我们将采用O (1)的插入复杂度和最多O (n)的移除复杂度。 通知列表中的听众将是我们列表中的重要任务。 由于我们需要通知列表中的每个侦听器,因此O (n)是我们可以避免的最快的复杂性。 在满足所有要求之后,我们可以开始考虑使用什么基础数据结构。 步骤2:设计 查看标准库中的可用内容,我们遇到了NSPointerArray —一个可以保存指向对象的弱指针的数组。 这满足了我们的第一个要求。 实际上,此数组几乎可以满足我们上面印刷的所有要求。 唯一不满足的要求是第二个要求。 当我们在其上调用.compact()方法时,有很多nil指针的情况下,运行时复杂度(基于实现细节的假设)最终将是二次方的,这不是我们想要的。 如果我们考虑自己设计自定义数据结构怎么办? 什么是合适的? 让我们看一下链接列表。 链接列表满足第一个要求,因为我们可以设计节点以保留对其对象的弱引用。 通过将新节点添加到列表的根端,我们可以在恒定时间内插入新对象。 删除对象将是O (n)操作,因为我们需要遍历列表并查找引用。 这是可以接受的,因为这是我们在要求中指定的内容。 通知我们的侦听器对象就像遍历列表一样简单,这是一个O (n)操作。 另外,我们可以使用相同的遍历查找零引用并将其删除。 如果我们在通知周期的一部分中实现删除,那么这将与重新分配一些节点指针一样容易,并且将是O (1)操作。 考虑一下,这种结构实际上听起来很适合我们的目的。 步骤3:实施 我们将花费一些时间来查看自定义结构的一些实现细节。 首先,让我们看一下链接列表的“协议”(严格来说,这只是出于说明的目的,因为为这样一个特定的实现制定协议实际上没有任何意义,并且其中包含了我们不会通常要公开)。 这些方法将是非常基本的,并且我敢肯定您自己实现这些方法不会遇到任何问题,但是我将花几行.notify()讨论一种很好的方法,以将一种清除列表的方法合并到.notify()方法。 协议WeakReferenceLinkedList { relatedtype数据类型:AnyObject var root:WeakRefNode ? func insert(_ object:DataType)->无效 […]