在这篇文章中,我们将研究引用类型和值类型之间的差异。 我们将介绍这两个概念,看看它们的优缺点,并研究如何在Swift中利用它们。 参考类型 引用类型 :一种类型,一旦初始化,将其分配给变量或常量,或传递给函数时,将返回对相同现有实例的引用。 引用类型的典型示例是对象。 一旦实例化,当我们分配它或将其作为值传递时,实际上是在分配或传递对原始实例的引用(即,它在内存中的位置)。 引用类型分配据说具有浅表复制语义。 在Swift中,使用class关键字定义对象: 类PersonClass { 变量名称:字符串 init(name:String){ self.name =名称 } } var person = PersonClass(name:“ John Doe”) 值类型 值类型 :一种类型,在分配给变量或常量或传递给函数时会创建新实例(副本)的类型。 值类型的典型示例是原始类型。 常见的基本类型(也是值类型)是: Int , Double , String , Array , Dictionary , Set 。 实例化后,当我们分配它或将其作为值传递时,实际上是在获取原始实例的副本。 Swift中最常见的值类型是struct , 枚举和元组可以是值类型。 值类型赋值据说具有深层复制语义。 复制语义 我将通过一个实际的例子来说明复制语义之间的区别。 假设我们正在使用通用树数据结构: 类Node { 出租价值:T var左:节点? var对:节点? 便利init(值:T){[…]} init(值:T,左:节点?,右:节点?){[…]} […]
今天,我的目标是开始着手从Spotify API检索JSON并将其转换为Swift对象。 我从未真正使用过新的Swift 4 Decodable对象来解码JSON对象,因此我决定学习如何使用它们。 我首先通过Lets Build That App观看了此视频(以两倍的速度): 我从他的视频中学到了很多东西,他在解释基本知识方面做得非常好。 您真正要做的就是拥有与JSON键相同的名称,并使类型与JSON值相同,Swift将为您完成工作。 您要使用的主要功能是: 尝试JSONDecoder()。decode(OBJECT_NAME.self,来自:数据) 但是,在解码时如何处理JSON对象中的JSON对象让我有些困惑。 这篇关于媒介的文章帮助我弄清楚了: Swift 4 Decodable:超越基础 全新Swift 4协议Decodable medium.com 幕后花絮 看来您只是使具有Decodable子类的其他结构具有相同的Key名称和值类型。 然后,当您调用JSONDecoder()。decode()时,它就可以工作。 老实说,这主要是魔术。 那篇文章还有其他一些很酷的技巧,我想改天看看。 我今天学到的另一件事是如何从命令行的Git有效地执行XCode中的“放弃所有更改”。 这些命令是: git clean -df git checkout-。 第一个命令删除所有新添加的文件,第二个命令放弃所有更改。 Github链接: harrysummers /无限图书馆 通过在GitHub上创建一个帐户为Infinite-Library开发做出贡献。 github.com
MVVM或模型,视图,视图模型的起源来自于2005年发布此文章的两名Microsoft开发人员: 用于构建WPF应用程序的Model / View / ViewModel模式简介 它修复了标准MVC或模型View Controller范例中的一些叙述空白,特别是消除了产生View Controller的副作用: 1)比他们应该了解的多得多 2)成为应用程序逻辑的重点 3)比他们指定的任务更加负责 View Controller变成了庞大的文件,难以维护,不是模块化且难以重新利用。 因此进入视图模型。 太从作者的角度出发 该术语的意思是“视图的模型”,可以被认为是视图的抽象,但是它也提供了视图可以用于数据绑定的模型的特殊化。 在后一个角色中,ViewModel包含将模型类型转换为视图类型的数据转换器,并且包含视图可用于与模型交互的命令。 通过改变逻辑和数据并将其放置在VM中,您可以在整个应用程序中重用视图的数据。 好吧,以后再深入。 面向协议的程序设计 。 苹果公司(源)一直在积极推动这个概念,我可以写一篇有关该主题的文章,但是要取得的成果以及我们将如何使用它非常简单。 为了创建具有严格功能限制的高度可重用的View模型,我们将创建协议来告知我们如何创建模型。 一旦有了该模型,我们将创建存储所有信息的视图模型,并将其传递给我们的视图控制器。 协调员 。 据我所知, 协调员一词是Soroush Khanlou在2015年创造的。我看到他的作品有很多风味,而其他出色的开发人员对此主题的看法也是如此。 如果想要裸露骨头,请严格按照POP方法检查Niels Van Hoorn,但到目前为止,我最喜欢的是AleksandarVacić。 协调器完全解决了UI层中的数据流。 它们不保存也不包含任何类型的应用程序数据-他们的主要关注点是将数据从中间件改组到前端UI。 他们做什么: 创建VC的实例 显示或隐藏VC 配置VC(设置DI属性) 加: 接收来自VC的数据请求 将请求路由到中间件 将结果路由回VC 他对协调器进行了更进一步的更新,进一步巩固了它们作为视图控制器和模型通信者的演示者的地位。 如果您查看Navigation Coordinator则它是UINavigationController子类。 协调员应通过其创建和管理的活动来命名,并应为包含这些活动的UI的VC呈现并触发信息的创建。 今天,我们将创建一个简单的登录应用程序。 具有LoginViewController , SetupAccountViewController和ForgotPasswordViewController一个。 这些都是与用户的帐户信息和身份验证交互的叙述,并且从这些视图控制器馈入和处理的数据非常相似: 1)有些表格可以接受用户数据 2)然后需要验证和处理此数据 3)如果数据未经验证,则需要通知用户其信息不正确 […]
欢迎来到“ 驯服iOS中的大量控制器 ”的最后一部分。 强烈建议您先阅读本系列的第1部分和第2部分,然后再继续本文。 在前面的文章中,我们将UITableView控件的数据源提取到了一个单独的类“ ShoppingListDataSource ”中。 这将所有数据源功能从ShoppingListViewController移到了数据源的指定类中。 这项技术极大地帮助我们减小了控制器的尺寸,并将其放置在正确的位置。 当用户选择购物清单时,他/她被发送到杂货项目屏幕,在该屏幕上显示所选购物清单的所有相关项目。 这意味着我们需要为杂货项目创建一个单独的数据源。 GroceryItemsDataSource看起来与ShoppingListDataSource完全一样,唯一的区别是GroceryItemsDataSource将处理GroceryItem类型的商品,而不是ShoppingList,如下面的屏幕快照所示: 一种替代方法是创建适用于所有类的通用数据源和数据管理器类。 通用的UITableViewDataSource和数据管理器: 通用的TableViewDataSource将独立于类实体,单元格或与对象关联的数据管理器/数据提供程序的类型。 UITableViewDataSource的声明如下所示: class TableViewDataSource :NSObject,UITableViewDataSource,FetchedResultsDataProviderDelegate { 如您所见,TableViewDataSource是一个通用类,您可以在其中传递单元格和实体的类型。 由于我们正在使用CoreData,因此我们的实体类继承自自定义协议ManagedObjectType,这是我们的自定义类型。 TableViewDataSource初始化程序采用所有必需的参数,如下所示: init(cellIdentifier:String,tableView:UITableView,dataProvider:FetchedResultsDataProvider ,cellConfigurationHandler:(CellType,Entity)->()){ self.cellIdentifier = cellIdentifier self.cellConfigurationHandler = cellConfigurationHandler self.dataProvider = dataProvider self.tableView = tableView super.init() } 需要注意的一件事是dataProvider参数,它也是FetchedResultsDataProvider的通用类型。 让我们检查ShoppingListTableViewController中的代码,该代码声明了数据源和数据提供程序。 私有var dataSource:TableViewDataSource ! 私人var dataProvider:FetchedResultsDataProvider ! 接下来,我们将使用适当的信息初始化dataSource和dataProvider属性。 覆盖func viewDidLoad(){ super.viewDidLoad() self.dataProvider = FetchedResultsDataProvider(managedObjectContext:self.managedObjectContext) self.dataSource […]
自动布局非常适合根据约束条件对布局进行调整,但有时布局需要根据设备类型,屏幕尺寸或方向进行更重要的调整。 也许您可能想要: 巨大的iPad Pro屏幕上的字体大小比小巧的iPhone SE更大。 在横向或纵向模式下,iPhone上的视图布局将有所不同。 在您的iPad版本的应用程序中提供其他按钮。 当您的应用处于滑行或拆分视图模式时,可以不同地布局内容。 您如何对布局进行这些调整? 在iOS的早期版本中,您可能有多个iPad和iPhone的情节提要。 或者,也许您已使用设备方向或窗口大小来确定用于布置场景的环境。 随着越来越多的设备,分割视图控制器(在iOS 8中引入)以及滑动和分割视图多任务模式(在iOS 9中引入)使调整布局以适应其环境变得更加复杂。 为简化起见,Apple建议使用新的范例。 而不是根据许多设备类型,分辨率,多任务处理模式和设备方向来考虑布局,您应该集中精力将布局调整为两种宽度(分别称为紧凑型和常规型)和两种高度(也紧凑型和高型)。定期)。 这些区别称为尺寸等级。 然后,您可以使用这些尺寸类来定义或调整布局。 尺寸等级将所有不同的潜在水平和垂直构型减少为两种:紧凑型,适用于有限的空间; 并经常在有限的空间内使用。 例如,iPhone纵向定位被认为具有紧凑的宽度和规则的高度。 有关尺寸类别如何与设备和设备方向相对应的全面细分,请参见图1。 在定义尺寸类别时,Apple做出了一些有趣的决定,值得注意: 在横向放置时,iPhone(6+和6S +除外)的宽度仍被认为是紧凑的。 所有处于纵向或横向模式的iPad都被认为具有常规的宽度和高度。 这意味着iPad方向的改变不会触发尺寸类别的改变。 尺寸类别不仅描述了设备类型和方向。 在iPad多任务处理模式下(例如“滑行”,“分割视图”和“并排”(见图2))显示应用程序时,大小类还描述了应用程序的环境。 请注意,尽管某些iPad多任务处理模式的水平尺寸类别可能会更改,但垂直尺寸类别仍保持常规。 实际上,紧凑的垂直尺寸级别足以表明我们正在横向模式下使用iPhone。 您可以使用Size类做什么? 大小类不能替代约束和自动布局,而是可以协同工作。 您可以使用尺寸类别对布局进行许多更改,例如: 可以调整视图的大小或位置。 字体和颜色可以更改。 可以激活或停用约束(称为已安装和已卸载)。 可以添加或删除视图(也称为安装和卸载)。 Interface Builder中的大小类 尽管您可以根据代码中的不同大小级别调整接口,但最简单,最方便的方法通常是使用Interface Builder ,尤其是在Xcode 8更新中添加的功能。您可以在Interface Builder中简单地将布局和约束分配给特定的大小级别,并且尺寸类发生变化时,布局也会自动更新-无需编写任何代码! 您将探索如何在具有非常简单布局的应用程序中的Interface Builder中为不同大小的类添加自定义项,并显示带有标题和正文的文章。 首先,您将构建iPhone界面,该界面在标签中包含标题,在文本视图中包含正文(请参见图3左侧的iPhone布局)。 但是,这种布局在iPad上看起来不太好(图3中央的布局)。 要解决此问题,您将使用尺寸类来调整界面(右侧布局)。 请注意布局之间的细微差别,而不是细微差别-随着iPad的空间增加,您将要调整字体大小,边距大小并添加字幕标签。 让我们从设置基本界面和约束开始。 创建一个简单的Single View Application,并将其称为SimpleSizeClasses。 […]
本文提高了对与异步代码相关的问题的认识,并提供了在Swift 3.0–3.1上进行编程时解决这些问题的示例。 样本问题的描述 这是源数据: Person是包含有关人员信息的结构的示例。 MyService是用作模型入口点的类的示例。 MyViewController是管理与UI相关的实例的类的示例。 MyService必须将Person提供给MyViewController以返回具有相应标识符的请求。 它可能在内存中没有所请求的信息,因此获取人员数据可能涉及网络,磁盘操作等。 回到同步编码时代 我注意到许多项目仍然使用同步方法。 因此,让我们首先使用它来解决示例问题。 扩展MyService { func person(identifier:String)throws-> Person? { 返回/ *从网络获取人员* / } } 似乎非常简单: input arguments -> output result 。 此方法可以返回该人(如果没有这样的人,则为nil);如果出了问题,则抛出一个问题。 它的使用方式就是这样: 扩展MyViewController { func present(personWithID标识符:字符串){ / *不要忘记调度到后台队列* / DispatchQueue.global()。async { 做{ 让人=尝试self.myService .person(标识符:标识符) / *不要忘记调度到主队列* / DispatchQueue.main.async { self.present(人:人) } } { / *不要忘记调度到主队列* […]
本文是我在东京-服务器端Swift聚会#5上演讲的幻灯片的英文摘要。 Swift中的并行性 DispatchQueue使我们能够轻松地在系统(或libdispatch)托管线程上进行多线程编程。 它具有两种执行属性。 serial :在单独的线程上按顺序执行任务 并发:在单独的线程上并行执行任务 哪种服务器架构适合Swift? 基于上述内容,让我们考虑一种适用于Swift的服务器架构。 当然应该注意C10K问题。 (这一次,我坚持使用Swift的标准API创建服务器。由于它应该适合Swift,因此对Swift程序员很友好,而且很生态。) 我的答案之一是… 基于DispatchSource的EventDriven模型+基于DispatchQueue的工作线程模型体系结构 通过这种体系结构,您可以不加意识地使用GCD提供的大量功能。 当然,没有自己的线程管理,没有回调地狱(通常)并且有效地使用了系统资源。 你喜欢它? GCD的问题 最后,我找到了Swifty服务器架构的答案之一。 但是..在GCD中……尤其是DispatchQueue有时会出现问题。 DispatchQueue问题 比赛条件 回调地狱 比赛条件 如果我们在并发模式下执行DispatchQueue.async并从多个队列访问变量,则可能发生竞争情况。 因此,我们必须对Mutex或Semaphore的每个操作都使用排他控制来避免这种情况。 但是,即使现在,独占控制仍然非常困难。 回调地狱 在事件驱动器范例中,回调地狱是一个严重的问题。 在Swift中,我们通常不必担心它,因为几乎所有操作都在阻塞。 但是,如果要异步访问文件系统或网络,还需要再次调用DispatchQueue.async。 我看到一个有10次嵌套回调的iOS应用… 避免回调地狱的现代方法 未来/承诺 异步/等待 但是Swift没有这些设备……所以我们应该放弃……? 不! 我们应该知道一种可以使用同步语法控制异步操作的语言……这是一种GO编程语言🐭 Go提供了称为Goroutines的原始并发/并行API。 它的行为就像在多个线程上多路复用的协程。 而Go可以避免Channel带来的比赛条件。 就像goroutine和goroutine之间的共享内存一样。 DispatchQueue也是基于线程的api,因此,如果我们将Goroutines和Channels之类的接口引入Swift,则使异步流控制变得容易,可读和安全。 简介 我实现了具有HTTP(S),TCP Server / Client和Go并发系统(称为Prorsum)的网络环境。 它的HTTP Server体系结构是EventDriven模型+工作线程模型,向您展示了上面的内容。 GitHub :https://github.com/noppoMan/Prorsum 像设备一样 使用Prorsum,我们可以像异步流控制一样轻松编写Go。 标杆管理 最后,我必须证明我所介绍的体系结构是否有效。 […]
大多数Swift开发人员不知道自己犯的错误 从Objective-C的背景开始,我感觉就像Swift阻碍了我。 斯威夫特(Swift)不允许我取得进步,因为它的类型强烈,这在过去常常令人发指。 与Objective-C不同,Swift在编译时会强制执行许多要求。 在Objective-C中放松的事情,例如id类型和隐式转换,在Swift中不是事情。 即使您具有Int和Double ,并且想要将它们加起来,也必须将它们显式转换为单个类型。 另外,可选是语言的基本组成部分,即使它们是一个简单的概念,也需要花费一些时间来适应它们。 刚开始时,您可能想强行打开所有包装,但这最终会导致崩溃。 当您熟悉该语言时,您会开始喜欢几乎没有运行时错误的方式,因为在编译时会捕获许多错误。 大多数Swift程序员以前在Objective-C方面都有着丰富的经验,除其他外,这可能会导致他们使用与其他语言熟悉的相同方式来编写Swift代码。 这可能会导致一些严重的错误。 在本文中,我们概述了Swift开发人员最常见的错误以及避免这些错误的方法。 没错-Objective-C最佳实践不是Swift最佳实践。 1.强制展开可选 可选类型的变量(例如String? )可能包含也可能不包含值。 当它们不持有值时,它们等于nil 。 要获取可选值的值,首先必须解开它们的包装 ,这可以通过两种不同的方式进行。 一种方法是使用if let或guard let可选绑定,即: var optionalString: String? //… if let s = optionalString { // if optionalString is not nil, the test evaluates to // true and s now contains the value of optionalString } […]
结果 :『Learn to Code1』 While循环 ,创建更智能的While循环→ 算法向右滚动,向左滚动 最后,我完成了『Learn to Code1』! 在今天的学习中,我学到了很多东西,并注意到了很多。 特别是,我了解代码非常强大。 例如,此代码令人印象深刻。 func navigationAroundWall(){ 如果isBlockedRight { moveForward() }其他{ 右转() moveForward() } } ^您可以在Medium中使用代码块:Cmd(Ctrl)-Alt-6 以下代码是我为最最终任务“ 算法向右滚动,向左滚动”的解决方案。 我知道,这不是好的代码和无限循环。 但是我解决了任务。 不知道为什么 当然,我必须对其进行调整。 func navigationAroundWall(){ 如果isBlockedRight { moveForward() }其他{ 右转() moveForward() } 如果isBlocked && isBlockedRight { 转左() } } 而!isOnGem { 而!isOnGem和&!isOnClosedSwitch { navigationAroundWall()} 如果isOnGem { collectGem() } […]
在我帮助开发的某些项目中,有时我需要控制UITextField在屏幕上的位置,主要是在使用大型可滚动屏幕时。 我从来不喜欢结果,因为我在视图控制器中放入了很多逻辑,而且我一直认为逻辑部分不应该存在。 因此,我想到了一种实现此目标的方法,现在我认为我有一个更好的解决方案。 我建立了一个从UIScrollView扩展的新类。 在此类内,我们具有所有逻辑指令,可以随时检测键盘何时调用活动,然后以预先设置的距离底部的距离控制内容。 为了在我们自己的项目中应用此解决方案,您要做的唯一任务是将类复制并粘贴到项目中。 在Xcode Identity检查器上,您必须使用名为“ KeyboardScrollView”的自定义类。 它既简单又实用。 我利用一个名为“ Extra Bottom Space”的属性作为IBInspectable,以允许程序员在IDE上设置一些底部调整空间。 将来可能要做的改进是自动检测键盘附件空间。 在此版本中,我没有考虑该增强功能。 因此,如果使用不同尺寸的键盘配件,但使用相同的键盘尺寸,则逻辑将失败,我的软件将按预期运行。 实作 演示项目 启发式/魔术键盘 MagicKeyboard –它是嵌入式UIScrollView实现,用于控制UITextField的定位。 github.com 希望对您有所帮助,欢迎提出任何意见或建议。