Tag: Iosdev

东京iOS聚会#swiftlang #iosdev

我想介绍一下在日本举行的iOS聚会。 东京iOS聚会 这次聚会主要是针对对iOS平台编程(iPhone,iPod … www.meetup.com) 感兴趣的人。 名为“ Tokyo iOS Meetup ”的聚会每月在日本东京举行。 我们尝试用英语交谈,因为它是一个国际社区。 我通常会说一点英语,这对我来说是极大的鼓励。 2016年12月10日举行了“ 12月iOS聚会 ” 。 3位演讲者做了精彩的演讲:

改进Swift中的完成块

从 Novoda博客 交叉发布 。 在Objective-C和Swift中,完成模块都是非常熟悉的模式。 这是一个有用的功能,它使我们可以在处理异步调用的同时将方法调用和生成的代码保持在一起。 可以在所有iOS代码中找到完成代码块,例如从URLSession提取的以下示例: let task = URLSession.shared.dataTask(with:aUrl,completeHandler:{(data,response,error)in //在这里处理结果 }) task.resume() 只要您避免回调地狱,它们是处理方法中单个结果的一种简短且易于编写的方法。 如果您有多个结果或来电,最好使用委托模式。 完成块有问题,但并不总是显而易见的,我们可以通过实现一个实现看到这些: let task = URLSession.shared.dataTask(with: aUrl, completionHandler: { (data, response, error) in if let data = data { parse(data.asJSON) } else if let error = error { display(error) } else { // no data and no error… what happened??? […]

用委托模式分离问题

在上一篇文章中,我们讨论了Closures的语法以及如何在其接收函数中使用它们。 今天,我们通过确保视图尽可能与控制器分离,来研究一种可用于在应用程序中实施分离和可扩展性的技术。 什么是代表? 委托模式被认为是解决关注点分离的一种可能解决方案,即任何对象都应处理自己的独特问题集。 例如,矩形结构不应处理计算圆的面积,依此类推。 这是一个非常明显的例子,但是委托的思想是一个对象应该使用专门的帮助对象来完成一项任务。 在MVC中,视图应负责向用户显示内容。 由于用户将通过单击,点击或按下视图来与视图进行交互,因此它们还必须接收输入事件。 但是,控制器的责任是实际解释输入并决定采取什么措施来响应输入,这是我们需要真正考虑设计的地方。 那么,如何在不使其中一个过于依赖另一个实现的情况下将视图的输入传递给控制器​​? 我们可以认为Controller在这方面是代表吗? 让我们从代码开始 让我们看一下我们将用于实验的类。 这是我们的BaseController,当我们要扩展应用程序时,它指定了一些不错的功能: 协议控制器{ 关联类型ViewType var typedView:ViewType {get} } class BaseViewController :UIViewController,控制器{ 覆盖func loadView(){ self.view = ViewType() } var typedView:ViewType { 如果让view = self.view为? ViewType { 返回视图 }其他{ 让view = ViewType() self.view =视图 返回视图 } } } 这是我们的BaseView,它为我们的View执行相同类型的操作: BaseView类:UIView { 便利init(){ self.init(frame:.zero) } 覆盖init(frame:CGRect){ […]

设计自定义数据结构的过程

在开发应用程序时,我们可能会遇到这样的情况,即我们无法使用的数据结构真正适合我们要完成的任务。 最近,我遇到了这种情况,正在开发一项功能,该功能要求使用事件监听器的集合。 今天,我想我会借此机会分享设计自定义数据结构背后的思考过程,并提出一些在实现它之前可能要回答的问题。 步骤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)->无效 […]

Swift内存布局简介

在上一篇文章中,我们讨论了Swifts 值类型语义的一些功能。 今天,我们将看一下如何分配不同的Swift类型以及这对我们的应用程序性能意味着什么的基础知识。 一个简单的类是Reference Type ,就像上一篇文章中提到的那样。 它具有一些我们大多数人都很早就了解的功能。 例如,单个类对象可以被多个变量引用和操纵。 但是,当我们创建一个类的实例时会发生什么呢? 类MyClass { var a:整数 init(a:Int){ self.a = a } } var A = MyClass(a:1) 上面的示例提供了一个简单的类,使我们可以开始研究创建引用类类型的变量A时发生的情况。 当我们调用MyClass初始化程序时,将进行一次调用以分配足够的内存以将我们的对象存储在内存中,在这里我们需要查看主内存的两个非常重要部分的第一个,即The Heap 。 堆是我们内存中用于动态分配的一部分。 这意味着如果在运行应用程序时需要即时创建一个新的引用类型对象,则需要在The Heap上请求空间。 由于内存的这一部分需要能够一次为多个线程提供服务而又不破坏任何数据,因此还需要一些机制来避免两次分配相同的内存块并处理并行分配调用。 在不详细介绍如何进行管理的情况下,让我们继续进行以下假设,即这实际上需要花费一些时间。 一旦找到足够的内存块并为我们分配了内存,我们的应用程序即可完成对对象的初始化。 除了初始化类中的实际值外,还需要初始化一些元数据值(其中包括类型数据和引用计数器)。 Swift使用一种称为自动引用计数(ARC)的方法来跟踪实际使用的对象。 如果某个对象未被任何变量引用,则可以安全地将其删除,并且可以将存储块返回以用于其他用途。 这是一个很棒的功能,但是它伴随着每次创建或共享对象时更新计数器的开销。 通常,如果您只是分配一个新对象,那么花费的时间就不会引起您的注意。 但是,如果您的程序不断创建这样的新对象,那么成本将会增加,并且您最终可能会浪费本来可以更好地用于其他用途的资源。 正如我在上一篇文章中提到的那样, struct和enum (以及其他)都是Value Types 。 我们需要问自己的问题是:将在哪里分配值类型? 这部分会有些棘手。 让我们首先看一下堆栈 -两个重要的主内存部分中的第二个。 堆栈是内存的一部分,它从其操作方式中获得名称-就像堆栈一样。 这个想法是,您有一堆数据,如果您添加任何新数据,它将被添加到顶部。 删除数据也被限制在内存空间的顶端,因此我们只能删除当前放置在顶部的数据。 系统使用一个简单的指针来跟踪当前堆栈内存顶部的位置。 这意味着分配和取消分配内存是通过简单的增量或减量操作完成的,因此非常快。 不利的一面是,这种结构与The Heap可以提供的动态分配类型不太兼容。 […]

了解Swift闭包

在上一篇文章中,我们讨论了面向协议的编程及其为我们带来的优势。 今天,我们看一下Swift闭包,并问我们自己如何利用它们的简短语法来使我们的代码更好。 什么是封包? 闭包是可以独立传递的功能块,可以在代码中传递和使用。 Swift中的闭包类似于C和Objective-C中的块,以及其他编程语言中的lambda。 上面的报价直接来自《 Swift语言指南》,尽管看起来似乎什么也没解释,但实际上是对闭包的非常准确的描述。 让我们看看当听到“ closure”一词时,大多数人可能会想到什么,即尾随闭包。 URLSession.shared.dataTask(with:url){(data,response,error)in //处理您从请求中获得的任何很棒的东西 } 这是一种很常见的尾随闭包。 在这种情况下,我们使用Foundation Framework中的函数对所需的某些数据进行异步请求。 然后,我们使用尾随闭包来指定我们希望在数据可用时要对其执行的操作。 对于不太熟悉闭包及其语法的开发人员,似乎我们只是无处不在地以某种方式提取了三个参数,它们神奇地起作用了。 为了了解为什么要指定这些参数名称,并使我们更容易使用尾随闭包来声明自己的函数,我们将看一下.dataTask(with:completionHandler:)函数的虚拟实现。 func dataTask(带有url:URL,completionHandler :(数据?,URLResponse ?、错误?)->无效){ //完成工作以获取我们请求的数据 让数据:数据? = {…} //获取负载响应 让响应:URLResponse? = {…} //如果未获取任何数据,则会产生错误 让错误:错误? = {…} //我们获得了所需的所有信息, //让我们将其发送给我们的闭包进行最终处理 completeHandler(数据,响应,错误) } 即使这是在此功能的实际实现中发生的情况的非常粗略的概述,它仍将准确地描述我们感兴趣的行为,即闭包如何在方法工作流中合并和使用。 看一下方法声明,在名为completionHandler的参数上,注意类型声明: (Data?, URLResponse?, Error?) -> Void 这告诉编译器,我们的.dataTask方法希望传递另一个函数作为参数。 它还希望该函数将三个非常特定的类型作为其自变量。 这些是我们在尾随闭包中命名的参数。 基本上,我们的程序告诉URLSession获取一些数据,然后指定将其删除的位置,以便我们可以继续进行更新。 DispatchQueue关闭 我们钟爱的DispatchQueue类还带有一些将闭包作为参数的函数。 看一下这个.async(execute:)调用: DispatchQueue.main.async { //执行更新等 […]

迅捷属性

什么是属性? 属性将值与特定的类,结构,枚举关联。 属性类型? 商店属性。 计算属性 商店属性规则。 它可以存储常量(let)和变量(Var)作为实例的一部分。 它只能使用类和结构。 注意:我们可以在store属性上提供默认值,并且可以在初始化期间为store属性修改初始值。 例:- struct FixedLengthRange { var firstValue:Int 令长度:整数 } var lengthOfrange = FixedLengthRange(firstValue:0,length:3) //范围代表整数值0、1和2 lengthOfrange.firstValue = 6 //范围代表整数值0、1和2 在这里,FixedLengthRange类具有名为firstValue的变量存储属性和名为length的常量存储属性,其值不能更改。 常量结构的存储属性。 如果我们创建结构的实例并为其分配常量。 这样我们就不能修改属性的实例,即使它们被声明为变量属性。 例:- 让 rangeOfFourItems = FixedLengthRange(firstValue:0,长度:4) //此范围代表整数值0、1、2和3 rangeOfFourItems.firstValue = 6 //即使firstValue是变量属性,这也会报告错误 计算属性规则。 1.可以定义类,结构和枚举 2.它提供一个getter和一个可选的setter,以间接检索和设置其他属性和值 如果在属性声明中将属性定义为get {},则会将该属性变为计算属性。我们可以使用两种类型的计算属性,一种是在属性内部使用get {},也可以同时使用get {}和set {} 。 如果我们将属性定义为get {},则称为只读计算属性。 例:- 使用get {} var […]

使用Swift属性观察器清理对象

在上一篇文章中,我们浏览了如何使用委托模式将视图与控制器分离。 今天,我们正在深入研究属性观察者的世界,以了解如何使用它们使对象更易于处理。 什么是财产观察员? 财产观察员观察并响应财产价值的变化。 每次设置属性值时都会调用属性观察器,即使新值与属性的当前值相同也是如此。 – Swift编程语言指南 以上解释几乎说明了一切。 在声明属性观察器时,我们这样做是因为我们有兴趣知道何时更改某个值。 引言告诉我们,“更改”并不一定意味着实际值已更改。 这个想法实际上是观察何时将变量写入其中,这可能是非常有用的事情。 除了观察并通知我们更改之外,我们还可以通过选择willSet (写之前)和/或didSet (写之后)观察者来选择希望在什么时间通知我们。 让我们看一些示例,这可以如何帮助我们清理代码并简化其他想要使用我们的实现的开发人员。 假设我们正在实现一个自定义进度条,该进度条将在您的应用程序的几个地方使用。 它由一个典型的蓝色条组成,该蓝色条随进度进行动画处理。 它还具有一个标签,用于向用户显示进度的四舍五入百分比值。 控制和强制变量的域 为了跟踪我们的进度,我们将使用Double类型的规范化值。 在这种情况下,归一化意味着我们将存储一个数字x,使得0≤x≤1。这种方法具有很多优点,尤其是在处理图形时。 它还负责检查设置的值,以使它们保留在域中。 让我们看看属性观察器如何帮助我们做到这一点: 类ProgressBar { public var progress:Double = 0 { didSet { self.progress = min(max(self.progress,0),1) print(“将新进度设置为\(self.progress)。”) } } } var p = ProgressBar() p。进步= 2 //打印“将新进度设置为1.0” 进步= 0.2 //打印“将新进度设置为0.2” p。进步= -100 //打印“将新进度设置为0.0” 很好 通过使用didSet观察器,我们可以确保进度值始终保持在其范围内。 […]

面向协议的编程简介

在过去的几年中,面向协议的程序设计已经获得了很大的关注,并在Swift社区中成为流行语。 有些人喜欢它,有些人讨厌它,但实际上是什么呢? 应该解决什么问题? 它与我们钟爱的面向对象编程有什么关系? 面向协议编程不是竞争对手,也不是面向对象编程的替代品,尽管它的名称可能暗示其他含义。 这是思考非常具体的问题集的一种方法,可以帮助您创建灵活,可维护和易于阅读的代码。 应该将其更多地视为对面向对象方法的补充(实际上,面向对象编程已经在面向协议编程中纳入了许多中心思想)。 我们将首先研究协议(或接口,在许多其他语言中称为接口)如何帮助我们进行封装和信息隐藏。 看一下Car和Carrier类的以下示例: 车类{ 变量位置:CGPoint var isLocked = true 初始化(位置:CGPoint){ self.position =位置 } 公共职能转移(x:CGFloat,y:CGFloat){ self.position.x + = x self.position.y + = y } 公共功能锁(){ self.isLocked = true } 公共功能unlock(){ self.isLocked =否 } } 运营商类{ 变量位置:CGPoint var loadingCars = [Car]() 初始化(位置:CGPoint){ self.position =位置 } 公共职能转移(x:CGFloat,y:CGFloat){ self.position.x + = x self.position.y […]

使用Swift3 iOS应用程序实现自定义相机功能

要在iOS中实现相机功能,我们使用Image Picker Controller,但如果需要更改Image Picker Controller以外的样式和功能,则需要创建自定义相机。 因此,在本教程中,我们将看到一个示例,该示例在iOS中使用Swift 3创建自定义相机。 让我们看看如何: 首先,我们将根据我们的需要或要求创建用户界面,并在控制器中包含3个元素:- UIView 影像检视 UIButton UIView用于向用户显示相机,UIImageView用于将单击的图像显示为预览,最后但并非最不重要的UIButton用于拍照并显示在UIImageView中。 屏幕的用户界面截图如下: 现在,我们将转向自定义相机功能的编码部分。 创建两个元素UIView的出口作为PreviewView,用于显示摄像机,另一个是ImageView作为captureImage,以显示捕获的摄像机的预览。 UIButton用于按钮动作,因此我们将不创建UIButton的插座,而将创建名为btnTakePhoto的UIButton动作。 在此处阅读更多有关如何使用Swift3 iOS App实现自定义相机功能的信息