iOS,swift,GCD,信号量 我今天遇到了一个问题,很想分享一下,以防有人遇到。 故事 我正在快速构建一个API,该API通过称为SendRequest的方法在内部向后端发送GET和POST请求。 此方法还可以交互并从数据库中获取数据(核心数据)。 在SendREquest方法内部,有一个GetLatestUpdateTime调用,该调用将GET请求发送到服务器并检索Date( LatestTime ),然后我从该Date( latestTime )之后的数据库中获取所有记录,然后将这些记录上传到POST中的服务器上。使用Alamofire提出要求。 问题 在非常特殊的情况下,可能在同一时刻多次调用SendRequest方法。 GetLatestUpdateTime将返回相同的lastTime对象,然后从数据库中获取相同的dbObject ,最后将这些相同的dbObjects上传到服务器。 重复 !!! 可怕的重复将发生,而这不应该发生。 解 在上一个调用完成之前,必须存在防止再次调用SendRequest方法的方法。 因此,调用是同步的,当然不会阻塞主线程。 GCD 与我一起工作的方法是将Dispatch Queues与Semaphore一起使用 。 首先,我创建了一个调度队列 var reportQueue = dispatch_queue_create(“ com.myapp.report”, DISPATCH_QUEUE_SERIAL); 2.和信号灯 让信号量:dispatch_semaphore_t = dispatch_semaphore_create(0); 零是这里的初始计数器,在我的情况下,我每次只想一次访问。 3.将sendRequest方法添加到队列 dispatch_async(self.reportQueue){ self.sendReport() } 4.在sendRequest方法中 并在调用GetLatestUpdateTime API之后,添加信号量等待 dispatch_semaphore_wait(信号灯,DISPATCH_TIME_FOREVER); 这将使初始计数器减少为-1 ,下一次对该部分“ sendRequest”的调用将不得不等待 。 5. 完成请求后,我们会发出信号量 dispatch_semaphore_signal(self.semaphore); 这将使计数器再次增加为0 ,从而允许再次输入关键部分。 这就是sendRequest方法的样子 公共功能exitRegion(){ […]
在上一篇文章中,我写过关于在Cocoa中为常规NSView设置背景色的信息。 因此,要想打个比方 ,我想分享如何制作NSViewController而不使用笔尖/ xib或情节提要。 这似乎是一件微不足道的任务,确实如此。 但是,在没有先验知识的情况下,您可能会很快发现一个基本的陷阱。 让我们在旅途中展示这个节目,看看一些代码。 就像上一篇文章一样,我将分享如何在iOS中完成此操作。 在这里,我们有一个名为MyViewController的NSViewController子类。 空视图控制器在这里没有什么特别有趣的。 非常简单! 让我们为macOS尝试相同的示例,看看会发生什么。 看起来一样,应该表现得一样吗? 好吧,也许有人会假定,但是,这会给您以下日志消息: -[NSNib _initWithNibNamed:bundle:options:]无法加载捆绑中的nibName:Apex.ViewController(空)。 就像消息中所述,您的视图控制器无法加载,导致其无法显示。 其原因是寻找显然不存在的nib文件。 由于接口构建器已深度集成到Cocoa的体系结构中,因此笔尖是macOS中的默认设置。 这意味着您必须做一些额外的工作才能使其正常工作。 幸运的是,它涉及的更改很少。 这里缺少的是视图控制器视图,要修复它,我们需要实现loadView方法。 因此,让我们看另一个示例,其中我们有一个不使用接口生成器的有效NSViewController实现。 就是这样,您现在可以在不使用NIB的情况下创建视图控制器。 编码愉快!
这是我在Medium上的第一个帖子。 什么是Slimane? 一个明确的启发性的Swift3微型框架和HTTP服务器。 https://github.com/noppoMan/Slimane 在对其进行介绍之前,我可以花一点时间讲讲故事为什么开始吗? 首先,在我的工程生涯中,我将大部分时间用于基础架构,后端,数据分析和Web开发。 所以我对Swift(尤其是iOS和Cocoa)的经验不足。 但是在2015年3月,我有机会为LifeClips(类似Blogging平台的中型平台)构建了一个iOS应用,然后我第一次使用Swift。 很快,我就爱上了Swift语法,例如强大的类型系统和足以与LL进行比较的灵活性。 迅速成为OSS 时间流逝…终于在2015年12月成为开源。然后我想我应该做点什么,那就是Slimane。 首先,这只是我的业余爱好。 尽管是业余爱好,但我每天都在做很多现代Web系统所需的功能/模块,有一天我意识到它可以在实际的Project中使用…(我正在等待Swift3发布) 特征 100%与事件循环后端(libuv)异步 独立的HTTP服务器 快点 模块化的 完全托管的异步流,TCP,管道,文件系统,进程,计时器等。 采用开放式快速 可用的中间件和模块 WS :Websoket服务器/客户端 衣架 :异步HTTP客户端 画眉 :轻量级的承诺实现 QWFuture :将来通过uv_queue_work实现 会话 :SessionMiddleware在Slimane应用中启用会话。 Redis :用于会话的Redis客户端和Redis存储 BodyParser :用于在HTTP请求的主体中解析JSON和formData。 TemplateEngine:小胡子和javascript模板 还有更多…https://github.com/slimane-swift 入门 目的是建立一个快速的服务器并尝试使用一些示例。 不用担心,您无需编写任何代码。 试试看! 安装 按照安装指南设置机器。 https://github.com/noppoMan/Slimane/wiki/Install-Guide 生成并启动服务器和应用程序 cd / path / to / your / slimane-example 进行调试 […]
什么是按需资源? 我们为什么需要它们? 我在寻找最小化iOS应用程序大小的方法时发现的这些问题的答案。 它是如何工作的? 很好地回答这个问题,对于我来说,给出一个测试用例来解释它是如何工作的将更加简单。 假设有一个游戏开发人员想要在iOS平台上制作游戏。 游戏包含5个阶段,每个阶段都有自己的资产。 这使应用变得with肿,资产增加了应用的大小,并延长了下载时间。 按需资源可以通过使资产与用户下载的应用程序捆绑包分开来帮助减小应用程序的大小。 这意味着用户可以下载较小的应用程序大小,并且开发人员可以自由地添加可以在应用程序内部使用的新资产,而无需担心应用程序大小。 怎么运行的 按需资源与标记资产一起使用,这些资产要与应用程序包保持隔离,作为识别资产的关键。 当需要资产时,操作系统将根据标签下载资产,并在需要时将其保留。 不再需要时,资产将自动清除。 按需资源具有3种类型的标签: 初始安装标签 ,这是在下载应用程序时下载的标签。 带有此标签的资产可能会影响appstore上的下载大小。 Prefetched Order Tags ,这是在安装应用程序后下载的标签,但我们不确定确切何时下载资产。 “仅下载按需标记” ,即在代码请求它们时正在下载的标记。 下载资产时,建议设置其下载优先级,以帮助用户体验。 如何访问资源 您应该做的第一件事是获取要访问的所有资源标签 让标签= NSSet(array:[“ tags1”,“ tags2”]) //设置要设置的数组 守卫让setTags =标签为? 设置 else { 返回 } 其次,您应该检查资源是否可用 让resourceRequest:NSBundleResourceRequest = NSBundleResourceRequest(tags:setTags)//检查资源是否已经可用 resourceRequest.conditionallyBeginAccessingResources {(resourceAvailable)在 如果resourceAvailable { //处理已经可用的资源 }其他{ //下载资源 } } 如果资源不可用,您应该下载资源,但是在此之前,您可以设置加载优先级,以便可以尽快下载资源, resourceRequest.loadingPriority = […]
在创作过程中,总会有一些选择和矛盾之处。 在UX设计师就Photoshop vs Sketch主题进行辩论并就有效的原型工具发表意见时,iOS开发人员也有自己的争论。 今天,我们将介入并在一些关于哪种编程语言是Objective-C或Swift的全球讨论中添加一些想法。 我们要求Tubik CTO和经验丰富的iOS开发人员Maria Nazarenko与Tubik Blog读者分享她的观点。 简要背景 让我们从一点点历史开始。 在讨论的一对编程语言中,第一个也是最老的是Objective-C。 它最初是由Brad Cox于1980年代初发明的。 因此,可以肯定地应用于这种编程语言的要点之一是,它确实经过了几代开发人员的反复试验和测试。 目前,Objective-C通常被描述为通用的,面向对象的编程语言,它将Smalltalk样式的消息传递添加到C编程语言中。 在Swift出现之前,Objective-C一直是开发人员用于OS X和iOS操作系统及其API的主要编程语言。 在2014年苹果WWDC上推出的Swift带来了自己的革命。 概括而言,Swift是一种通用的多范式编译程序语言,它是为iOS,OS X,watchOS,tvOS和Linux开发的,拓宽了以前的操作系统范围。 Swift旨在与Apple的Cocoa和Cocoa Touch框架以及已经为Apple产品编写的大量Objective-C代码一起使用。 最初,Swift的目标是比Objective-C更具弹性和安全性,并且更加简洁和友好。 它是使用LLVM编译器框架(包括Xcode 6)以及使用Objective-C运行时库构建的,该库使C,Objective-C,C ++和Swift代码可以在一个程序中运行。 因此,它为开发人员扩大了专业视野,使他们的工作更有效率。 这是开发人员开始讨论更好,传统或创新的里程碑。 哪个更好:Objective-C还是Swift? 前段时间,我们在关于Photoshop和Sketch之间选择的热烈的全球讨论中表达了我们的看法:“ 我们要给出的答案确实很简单。 可以互相补充的选项之间不应有任何战争。 显然,现在没有任何完美而理想的设计软件:如果存在,那么每个人都只会使用这一理想选择,而无需战斗。 » 似乎在iOS开发中,今天的情况确实相似。 即使阅读完Swift与Object-C的所有扩展功能之后,选择似乎也不是那么明显。 实际上,在Swift尚不成熟的时候,Objective-C便经过了多年的检查和测试。 在Objective-C中已经开发了大量产品,因此过渡到其他视图肯定需要花费一些时间和精力。 根据她在iOS开发中的丰富实践经验简要地描述了编程语言,Tubik CTO Maria提到,尽管Objective-C年龄较大且很常见,但是Swift可以被描述为语法更简单,工作过程更稳定。 哪种编程语言更有用? 由于Xcode是带有一组软件开发工具的集成开发环境(IDE),iOS开发人员实际上可以使用同一开发人员工具来应用Objective-C或Swift。 只要Objective-C存在更长的时间,使用这种语言自然就可以完成许多简单和复杂的应用程序。 不了解Objective-C并直接从Swift开始会限制开发人员的专业灵活性。 在开发人员必须更新现有产品的情况下,这一点尤其明显。 有时会发生这样的情况,开发人员只知道Swift,建议将现有的(也许是大的甚至是巨大的)代码从Objective-C重写为Swift,客户在逻辑上不认为这是可以接受的,当然也不接受为此付出代价的必要工作。 考虑到这一点,最好的选择是同时了解两种语言。 这为开发人员提供了自然的专业灵活性,使其可以处理当前项目中必须处理的任何任务和环境。 有两种策略主要取决于可用时间。 如果在学习基础知识和将其应用到实际实践之间花费了大量的时间,我们的建议是从Objective-C开始。 尽管它通常被描述为更复杂,但是它为创建和使用iOS系统的应用程序的基本原理和例程奠定了坚实的基础。 在此基础上,学习语法上更简单的另一种iOS语言(例如Swift)似乎并不难。 但是,还有另一种策略,它比以前的策略更具动态性,适用于那些没有足够时间处理需要尽快开始实际工作的人。 […]
问题 在本系列的第一部分中,我定义了我认为是“良好”的单元测试。 这是我最终得到的定义: 如果我们可以同意一个单元测试(或者实际上是任何一个测试)是由一些设置 ,我们正在测试的动作以及关于该动作效果的断言组成的,那么我可以这么简单地说,一种“好的”单元测试可以使这三个组件中的每一个都清晰可见。 您可能还记得,我们以如下所示的测试结束了这篇文章: var sut,其他:用户! func test_equals_allPropertiesMatch_isTrue(){ (sut,other)=(.create(),.create()) XCTAssertEqual(ut,其他) XCTAssertEqual(other,sut) } func test_equals_nameDiffers_isFalse(){ (其他)=(.create(name:“ Jo”),.create()) XCTAssertNotEqual(sut,其他) XCTAssertNotEqual(other,sut) } 我们压缩了设置,以便测试主体仅包含对测试场景重要的信息。 例如,当重要的是两个用户对象的名称不同时,我们在设置中仅包括该信息。 这是提高测试总体可读性的好方法。 但是,我们还有更多可以做的事情。 您可能查看了上述测试,并认为“设置还可以,但是为什么在这里却有两个断言呢?”很好的问题! 我很高兴您关注这个问题。 请记住,以上测试涵盖了User类型上的==函数。 在不给您带来全部数学负担的情况下,平等是“等价关系”的一个示例,而关于等价关系的重要内容之一就是对称性的概念。 简而言之,如果我有两个某种类型的实例a和b ,则永远不会出现a == b为true但b == a为false的情况。 如果这是可能的,那么我们对平等的定义是有缺陷的。 因此,有必要验证我们的==的自定义定义是对称的。 但是,仅看那里的两个断言,就完全不清楚这是意图。 这些测试只是在断言一件事。 经过一段时间和倾向,其他工程师可能会弄清楚为什么我们添加第二个断言,但是我们绝对可以做得更好。 但是…如何? 解决方案 让我们开始做我们可能想到的最简单的事情。 我们有两行代码,而我们只想有一行代码。 解决方案? 一个功能! func assertSymmetricallyEqual(_ sut:用户,_其他:用户){ XCTAssertEqual(ut,其他) XCTAssertEqual(other,sut) } (注意:这里我们专注于测试是否相等,但是对于不平等,您可以做完全相同的事情)。 现在我们的测试变为: var […]
在Tiendeo Mobile部门,我们一直在寻找新的方法来使我们的应用程序更具可测试性和更好的测试。 让我介绍有关数据层的基本概念。 如果您熟悉Alamofire和AlamofireObjectMapper,则我们的应用程序数据层将使用一个名为AuthenticationSessionManager的自定义Alamofire SessionManager类,该类可实现RequestAdapter和RequestRetrier协议。 我们的AuthenticationSessionManager有自己的请求方法,可以在调用Alamofire请求方法之前检查用户令牌是否有效: 首先,我们创建一个称为AuthenticationSessionManagerTest的单元测试测试类,并实现一个方法,该方法返回无效令牌,并使用Kakapo的Router类实现另外两个方法来设置拦截器,用于两个成功的API调用: 然后,我们需要配置我们的自定义SessionManager以使用Kakapo协议。 我们将创建一个方法来创建自定义URLSessionConfiguration : 并且…我们已经完成了测试的// Given部分: 总结一下:我们已经有一个配置有Kakapo和无效令牌的sessionManager 。 我们还有两个API调用可供拦截。 💪 现在我们必须实现测试的// When和// Then部分。 我们将使用sessionManager向“ _api / Fake”发出请求,该请求将被拦截。 正如我们在文章开头所看到的sessionManager , sessionManager将首先检查令牌,但是令牌将无效…… sessionManager自动向“ _api / TokenNew”请求新的令牌,该令牌也将被拦截。 当获得新令牌时,最终它将调用“ _api / Fake” ,其响应将成功。 🎊 让我们看一下代码: 这是测试的样子! 我们使用expectation来检查响应的值是否与我们为Kakapo路由器设置的值相同。 从这里开始 以此测试为样本,我们可以创建多个测试方案来测试我们的SessionManager,如下所示: 有效令牌 伪API调用成功 要么: 没有令牌 刷新令牌API调用成功 伪API调用成功 要么: 有效令牌 伪API调用响应401 等等 只是改变 //给出条件并向Kakapo的路由器添加新的响应。 希望您发现这篇文章有趣并且对您的项目有用。 任何问题或评论都将受到欢迎! […]
名词定义: ARC : 自动引用计数( Automatic Reference Counting )是编译器的记忆体管理功能,为Objective-C和Swift提供自动引用计数。在运行时增加和减少引用计数,当类实例的引用计数达到0时释放这些类实例。 有力的参考: 当宣告一个类实例给一个属性时,预定为强引用,引用计数增加。 弱引用(弱和无主): 保护不了所指的类实例不被ARC回收的引用,弱引用不能让引用计数增加。 例: 下图是简单的参考周期示意图,变数John跟随unit4A各别指向各自的实例,而john的属性公寓指向unit4A实例,而unit4A的属性租户也指向john实例,所以目前john instance跟unit4A的引用计数都是2。 现在把约翰跟unit4A变数设为nil: 现在john跟unit4A的instance都无法被存取到,但只有另一个的instance,因为reference count都还各是1无法释放,造成内存泄漏。 使用弱引用解决,将公寓的租户宣告为弱打破强引用周期: 弱var租户:人? 现在将john = nil 因为John的实例只剩unit4A的租户指向,但tenant是弱引用,引用计数不会增加,所以john实例的引用计数= 0,john实例被释放。 无主跟弱者一样都是弱引用不同的地方在: 重要 仅当确定引用始终引用尚未取消分配的实例时,才使用无主引用。 如果在释放该实例后尝试访问一个未拥有的引用的值,则会收到运行时错误。 在他的生命周期中不会转化nil,反之使用弱。 在实际开发时最常遇到保留周期的地方是在使用闭包跟代表时。 使用closure时,如果body里面有用到自我 使用unowned去打破周期:(仅当段有此实例存在才有可能使用闭包asHTML,所以可以使用unowned): 委托举最常用到的TableView委托当范例当ViewController拥有tableView而tableView的委托创建自己拥有ViewController如下图: VC:ViewController S:有力的参考 W:参考不足 在Apple开发人员文档中也可以看到UITableView的代理是宣告为弱参考来中断周期:
我最喜欢的是蓝色。 如此宏伟的生物……。 虽然如此,尽管他是一位伟大的自然爱好者,拥有所有的生物,但我对动物学的了解不足,不会让我进一步感到羞耻。 当然,我将谈论VIPER客户体系结构。 这个故事不是另一个“ iOS上的VIPER简介”或当前其他任何地方。 我相信我们有很多有关该主题的文章。 假定您了解该概念并具有一定的经验。 这也不是要将其与任何其他架构方法或架构进行比较。 我之所以选择它,是因为它确实代表了一种相对较好的“ 清洁体系结构”方法,并且遵循SOLID原则,可以承受。 我什至不会自己使用首字母缩写词,但是我想它使我们所有人都更容易理解该主题。 这个故事是关于以更多样化的方式展示VIPER,并展示其友好的面孔。 为此,我将首先处理一些神话,然后再讨论一些问题,考虑真正的问题,下一次我们将研究一个演示项目,该项目将作为一个可能的解决方案/答案。 误区1:VIPER有很多课程 可以? 您认为应该有几节课? 或更准确地说:您希望在代码中包含多少“ 单一职责原则” ? 也许所有这些都值得商bat。 即使采用SR原则,我们也可以很快就某个特定类的职责进行辩论。 例如,Microsoft在MVVM的定义中将视图模型视为胶水代码 ,作为业务逻辑和视图之间的中间人。 有人可能会争辩说,这种职责范围太广:处理事件,表示逻辑并将模型数据转换为易于查看的形式应视为单独的职责。 关键是:无论采用哪种体系结构,如果要使代码可维护且可靠,那么它将必须符合“干净代码”的许多范式,其中包括SR,小类,尽可能少的耦合,OO模式,全部资金。 ……所有这些都导致“……很多课程……” 。 误解二:僵硬,有很多样板代码 我记得几年前,当我第一次接手VIPER并接管现有项目时,这并不是最好的体验。 不是因为VIPER本身,而是因为与该概念的明显斗争。 我实际上看到了我在这里所说的一个神话: 每个模块都需要很多样板代码 很多时候,这些类都没有创建,只是为了“遵循方法” 开发人员努力地采用需求的逻辑以使其适应VIPER规范,这当然是行不通的,并导致一些严格的遵循并同时破坏它的怪异组合。当我说“破坏”时,我真的这意味着从破坏模块的封装为一的角度来看。 演示者或路由器双向连接太多对象,并使每个结构更改都成为真正的PITA。 我们将在稍后的演示项目中看到,如果您牢记这一点,则不必一定是这样: 您需要遵循的是VIPER所基于的SOLID和其他OOP原则,并据此,您应该创建适合您工作的类。 尽可能在协议扩展,抽象类甚至父类(如果不是太多!!)中实现样板 误区3:学习困难,仅适合高级开发人员 VIPER就像学习SOLID,TDD和OOP设计模式一样困难。 等等…但这还是我们要做的,不是吗? 好的,我也可以添加FRP 。 但是没关系,您的意思是:如果您想成为一名真正的高级开发人员,那么您必须学习上述知识,在这种情况下,您应该编写VIPER编码就像将比萨饼切成小块并撒上手工啤酒一样简单。选择。 是的,但是是初级人员……好吧,那么,如果您的初级同事没有采用VIPER风格,那么您的代码库将采用初级人员的技能组吗? 如果是这样,那么是的,VIPER仅适合高级开发人员。 但是,在我曾经工作过的大多数地方,我们都确保初中生由年长者通过代码进行培训,而并非相反:采用代码以适应最高的通用技能水平…… 误解四:与SDK规定的MVC基础架构配合使用时,效果不佳 我想我在面向模块的体系结构系列中再次揭穿了这一点,特别是在第6部分:超越MVC中。 您也有类似Uber RIB的框架,也可以解决该问题,但是我不得不承认,我个人认为不应将体系结构转变为即时框架解决方案,但这是重点。 误解5:不适合小型项目或原型 同样,在面向模块的体系结构系列中,我们可以看到VIPER类完全精简和小巧,因为大多数样板都在协议扩展中实现。 如果您遵循神话2中所述的方法,那么即使在小型项目和原型中,您也会发现它非常易于使用并且可以快速实现任何种类的特定功能。 […]
(((((()-> {})-> {})-> {})-> {})-> {} 好。 这篇文章有点愚蠢,因为我将使用另一个高阶函数编写一个高阶函数,以便向您展示什么是高阶函数。 我希望我不会犯任何错误。 高阶函数是将函数作为其参数的函数。 好吧,我认为该陈述并不能解释“全部”,但这是我可以向任何非编码者朋友解释的最简单的方法,所以请问。 无论如何,许多编程语言中都存在高阶函数。 我将在本文中使用Swift,但是所有其他语言的概念应该相同。 让我们深入研究。假设我们有一个整数数组。 让数组= [1,2,3,4] 这是将所有元素乘以2的Computer-science-101方法,这是 var newArray = [Int]() 对于0中的i .. <array.count { 让element = array [i] * 2 newArray.append(element) } print(newArray)// [2,4,6,8] 实际上,这里的for循环可以被forEach函数代替,后者是Swift附带的高阶函数。 甜… var newArray = [Int]() array.forEach({ newArray.append($ 0 * 2) }) print(newArray)// [2,4,6,8] 请注意,我们没有将0乘以2。 $ $ 0只是保留的简短形式,表示闭包的第一个参数。 不要问我为什么是$ 0。 […]