Tag: swift

查找并返回Swift中给定字符串的所有出现范围

在谈论字符串模式匹配识别问题时,您想到的第一个想法可能是Knuth–Morris–Pratt算法。 但是,在本文中,我想介绍一种基于range(of:options:range:locale:)的更直观的方法,该方法是Swift标准库的一部分。 多次返回第一次出现以收集所有它们 如果您查找Apple Developer Documentation,则会发现对函数range(of:options:range:locale:)的描述,该描述与下面的引用相同。 使用给定的语言环境(如果有),在给定的选项的约束下,找到并返回给定字符串在给定字符串范围内第一次出现的范围。 是的,它只返回第一次出现 。 因此,为了实现我们的目的,我们需要构造一个while循环以迭代接收器中给定字符串的所有出现。 参考代码段将类似于以下代码块。 并请注意,还添加了偏移量以减少每次迭代中的范围长度。 因此,实际上,每个循环的位置基本上都是基于上一次迭代的上限。 extension String { var indices = [Int]() while let range = range(of: occurrence, range: position..<endIndex) { to: range.lowerBound) let offset = occurrence.distance(from: occurrence.startIndex, guard let after = index(range.lowerBound, limitedBy: endIndex) else { } } } 将索引转换为范围 在将上述代码应用于实际情况时,有时返回所有索引不足以进行文本编辑中的查找和替换所有操作。 因此,转换助手可以帮助生成范围数组,该数组更易于处理子字符串。 extension String { let […]

关于Swift可选内容的一切

Swift带来了许多不是Objective-C一部分的新功能。 这些功能之一是Optionals 。 虽然Objective-C提供了一种将变量标记为nullable ,但是Swift在全新的水平上实现了可选方法。 在深入研究细节之前,让我们首先讨论为什么首先需要它们。 数据是任何软件的关键部分。 在现实世界中,并非始终保证数据可用性。 例如,由于某些网络问题,对API的调用可能会失败。 作为开发人员,您需要以友好的方式处理此类情况。 换句话说,有时,我们有时会拥有或可能没有有效数据。 这就是可选的背后的核心思想。 让我们详细讨论它。 在Swift中,可选选项可让我们处理可能具有合法值或没有合法值的情况。 乍一看,这似乎很明显,因为在许多其他语言中,常规数据持有者会遇到这种情况。 例如在Java中: 字符串消息=“你好,世界!”; System.out.print(str); // “你好,世界!” //可以将null / nil分配给常规字符串持有者 message = null; System.out.print(message); // 空值 在Swift中,这是非常不同的。 无法将常规Swift变量分配为nil 。 因此,要表达缺少值,您需要使用可选。 实际上,这是您唯一可以在Swift中分配nil东西。 要将数据成员声明为可选,需要添加? 在数据类型之后。 var id:Int id = nil //编译errorvar optionalId:Int? //可选的Int optionalID = nil //合法的Swift表达式 这是有道理的,因为如果甚至有几分之一的数据丢失的可能性,您就需要在那里表达它。 许多开发人员不时犯下的一个常见错误是,以一种脆弱的方式访问变量,该变量没有任何合法的值,并最终导致运行时崩溃。 使用可选选项时,它会在编译时处理。 可以将Optional看作是强类型容器,它包含一个值或否则为nil 。 如果它包含一个值,则可以解开容器并使用基础值。 为此,您需要使用一些额外的信息,这些信息确定如何解开基础值。 […]

功能编程:闭包参考周期和修复

Article_1链接→ 功能迅速:关于闭包的全部。 Article_2链接→ iOS中的Lazy var swift。 请阅读以上两篇文章以继续。 假设您对函数式编程和闭包有很好的了解,那么让我们开始吧。 考虑一类Human 。 它具有一个init方法,该方法接受firstName和lastName作为参数。 它还有一个名为fullName的lazy var closure ,该lazy var closure不接受任何内容并返回String 。 它是()->String类型的。 在这里,当我们调用init方法时,将创建一个新的Human对象。 我们将此对象分配给可选类型humanObj 。 在下一行中,当我们将humanObj的值设置为nil ,将deinit{}方法。 您可以看到控制台打印在deinit方法中给出的文本。 现在,尝试在创建对象之后访问fullName闭包。 然后尝试将对象设置为nil 。 var humanObj:人类? = Human(firstName:“ John”,lastName:“ Doe”) let fullName = humanObj?.fullName humanObj = nil 由于闭包强烈引用self因此不会调用deinit{}方法。 释放对象时将调用deinit方法。 解决方法 我们可以在捕获列表中使用self来使用weak引用或unowned引用来打破强引用周期。 如果您不知道什么是捕获列表,请阅读顶部提到的文章。 弱 弱引用是指不会对其引用的实例保持强大控制的引用,因此不会阻止ARC处置所引用的实例。 此行为可防止参考成为强大参考周期的一部分。 由于弱引用可能为nil ,因此捕获的变量变为可选。 因此,我们应该使用guard来安全地拆开它: 懒惰的var fullName :()->字符串= […]

Swift中函数的通用专业化

在今天的帖子中,我们将讨论Swift编译器的Generic专业化知识,并对它是什么以及SIL Optimizer为什么执行此操作以获得更高性能的代码进行基本概述。 此外,我们还将简要介绍Swift中间语言(SIL)表示形式以及它在此通用专业优化遍历中的作用。 首先… 编译器 在深入研究泛型专长之前,我们需要至少从高层次上了解有关编译器流程的一些知识。 swift编译器获取您的Swift代码,将其处理为手动编码的Lexer,将其标记化并将其转换为抽象语法树(AST),然后是语义分析(Sema),其中编译器将使用解析器生成的AST并进行类型检查的AST并检查其中的语义问题。 然后,Swift中级语言生成(SILGen)阶段将通过语义分析生成的AST转换为他们所谓的原始SIL,在对SIL进行了一些优化(如通用专业化,ARC优化等)之后……它生成了这种被称为规范SIL的优化SIL。然后将其交给IRGen生成中间表示(IR),该中间表示将传递给LLVM,以使其继续工作并生成目标文件(.o),该文件随后将由链接器粘合在一起并生成最终的二进制文件。 这是对编译器管道的简要概述。 这只是让我们了解编译器的各个阶段,但是在这里,我们将重点介绍发生通用专业化的SILGen和SIL Optimization Pass。 仅供参考,Swift中间语言(SIL)是一种静态单分配形式(SSA形式)中间表示形式,用于实现swift编译器,目的是在源代码与LLVM IR之间架起抽象桥梁,从而提供一种执行高级别的优化,例如通用专业化,保留/发布优化,动态方法去虚拟化等等……以及高级诊断分析。 您可以在快速主存储库的说明文件[1]中找到有关SIL的更多信息。 返回通用专业化 因此,泛型专业化是SIL优化过程,它分析了对泛型函数的所有调用(使用完整模块编译模式时在模块中)或具有@_specialize属性的泛型代码(我们将在后面讨论),然后生成新的特殊化这些函数的版本将泛型的所有专用用法替换为直接调用生成的专用函数。 使用专用函数的性能更高,因为它可以使用特定的具体类型最有效的布局,并且在诸如将参数复制到局部变量之类的操作中,例如,它避免了动态分配泛型类型和保存对运行时类型元数据大小和对齐方式的值见证表。 让我们看一下如何生成专用SIL的实际示例 生成的SIL上还有很多其他内容,但仅突出显示与我们定义的函数相关的内容。 仅查看生成的SIL,我们就可以看到编译器如何发出通用和专用SIL表示形式。 我们可以注意到,主SIL调用了函数@ $ S4main9incrementyxxs13BinaryIntegerRzlFSi_Tg5的引用,正如我们看到的那样,它是增量的专用版本。 这意味着主要功能正在调用优化的专用功能。 另外,我们可以注意到专用版本针对Int进行了完全优化,而在通用SIL上,所有操作都基于基于元类型信息的调用(应用)函数。 告诉SIL优化器不执行专业化 我们可以告诉编译器,由于某种原因,我们不希望使用@_semantics属性对该通用函数进行专用化。 您可以在HighLevelSILOptimizations文档的“优化语义”属性部分中找到有关语义的更多信息。 这就是本文的全部内容\ o / 如果您有任何意见或疑问,请告诉我。 您的反馈意见非常重要,因此我们可以改善此问题以及将来的帖子,很高兴收到它:)) 您可以在Twitter上@ LucianoPassos11找到我。 感谢您阅读🙂 参考文献 Swift中级Languange(SIL)。 https://github.com/apple/swift/blob/master/docs/SIL.rst SIL程序员手册。 https://github.com/apple/swift/blob/master/docs/SILProgrammersManual.md 泛型。 https://github.com/apple/swift/blob/master/docs/Generics.rst SIL中的高级优化。 https://github.com/apple/swift/blob/master/docs/HighLevelSILOptimizations.rst

雨燕:带有动画的渐变

要向视图添加渐变效果,您需要创建CAGradientLayer类的实例,并将其作为视图的子层添加。 这意味着您实际上将使用 UIView 实例 的子层 ,而不是视图本身。 这是我们的具有渐变背景的视图: targetView = UIView() targetView.translatesAutoresizingMaskIntoConstraints = false targetView.clipsToBounds = true targetView.backgroundColor =珊瑚玫瑰 targetView.layer.cornerRadius = 8 view.addSubview(targetView)targetView.leftAnchor.constraint(equalTo:view.leftAnchor,常数:35).isActive = truetargetView.rightAnchor.constraint(equalTo:view.rightAnchor,常数:-35).isActive = truetargetView.centerYAnchor.constraint (equalTo:view.centerYAnchor).isActive = truetargetView.heightAnchor.constraint(equalToConstant:270).isActive = true 创建渐变效果 现在我们准备添加一些将渐变效果应用于视图的代码。 首先,我们需要添加一个CAGradientLayer类型的新属性。 我们将设置图层的框架和颜色以产生渐变效果。 让backgroundGradientLayer = CAGradientLayer()func applyGradient(){ backgroundGradientLayer.frame = targetView.bounds backgroundGradientLayer.colors = [reynard.cgColor,coralRose.cgColor] targetView.layer.addSublayer(backgroundGradientLayer) } 点击“ 应用渐变”按钮,查看结果: CAGradientLayer的colors属性是一组将定义渐变效果的颜色。 不幸的是,该属性需要一个 AnyObject 数组 ,但仅适用于 CGColor […]

Swift 4.0 Codable-解码子类,继承的类,异构数组

Swift 4.0引入了一个非常有用的API。 如果您一直在关注Swift项目和该版本中使用该语言的新功能,则可能已经听说过。 在使用某些REST API时,我遇到了一些问题,因此想在这里记录它们,以防其他人遇到相同的情况。 如果您熟悉Codable ,则可以跳过本节,如果不是,这就是文档页面上Apple汇总的方式: 使您的数据类型可编码和可解码,以与JSON等外部表示兼容。 关于如何使用此新API,已经有很多不错的文章。 这些是最好的,我建议阅读它们作为切入点: 编码和解码自定义类型 Swift 4 JSON解析终极指南 Swift 4 Decodable:超越基础📦 异构数组是指您拥有一个包含多个不同对象的JSON片段。 在我的情况下,这些对象是基类Drink子类。每个子类具有不同的属性,需要序列化。 首先,需要定义模型,符合可Decodable协议。 从基类开始,然后是子类。 这是基类,定义了与之关联的所有属性。 这没有什么复杂的,因此不需要编写自定义代码,它只使用标准API。 Beer还具有alcohol_content属性,因此我们需要添加它。 此类需要在基类之前进行初始化。 我们需要编写自己的自定义init方法。 您可以在第11行看到,我们将相同的解码器对象传递给超类,以便它可以对基类属性进行解码。 这就是我们将json转换为内存的方式。 这里没有什么复杂的,但是我们现在需要定义Drinks对象。 该对象包含一个异构数组,因此我们需要自己实现Decodable协议。 第2行显示我们将Drink对象的数组作为属性添加到此类。 接下来,我们创建一个枚举DrinksKey ,该枚举用于解码包含饮料数组的字典。 我们还定义了一个枚举DrinkTypeKey ,它用于序列化数组中每个对象的类型字段。 最后的枚举DrinkTypes与指定所有可能的饮料类型。 使用所有指定的枚举,我们可以进行序列化。 第19行:获取饮料字典- container 第20行:获取饮料数组— drinkArrayForType 第23行:复制drinksArray数组— drinksArray这是必需的,因为我们要迭代drinkArrayForType 。这样做时,我们将无法获取数组中每个元素的解码器对象。 请参阅下面的摘要。 第24行:迭代drinkArrayForType容器 第25行:获取当前的饮料容器 第26行:解码type属性 现在,我们可以使用此解码属性来标识要解码的类。 但是,我们必须使用复制的drinksArray对象进行解码,因为drinksArrayForType的迭代器已移至下一个对象。 中提琴! 最后一步是我可以获得Decodable API进行出价的唯一方法。 据我所知,没有办法从UnkeyedDecodingContainer或KeyedDecodingContainer获取解码器。这似乎是对API的限制,但我肯定有充分的理由使其无法使用。 仅在这种解码用例中公开这种依赖性似乎有些奇怪。 […]

UITableView入门(有关编写简洁代码的提示)

将表视图添加到视图控制器并设置约束,如下所示。 拖动Table View Cell,并将Style设置为Right Detail,并将单元格标识符设置为’cell’。 稍后我们将相应地进行调整。 我通常按​​照上面显示的方法处理表视图,只是为了给我更大的灵活性。 运行模拟器,您可以看到我们的数据列表已正确填充。 请注意,我们正在使用提供给我们的4种基本样式之一。 继续尝试将tableview 样式更改为Subtitle,然后重新运行模拟器。 很好! 您已经使用一些数据创建了第一个表格视图! 我们已经获得了符合UITableViewDataSource和UITableViewDelegate协议的UIViewController,并将其设置为UITableView的数据源和委托。 这仅适用于几行代码,但可以想象如果UIViewController以后在应用程序中有更多事情要做,该类可能会变得肿。 它将承担太多责任,并且只会使其难以维护,阅读或测试。 因此,让我们继续重构代码。

Swift 4中的泛型–开发人员思想–中

以良好的格式阅读此文章,并在 Swift Post 上突出显示语法 。 作为Swift中最强大的功能之一,泛型可能会很棘手。 很多人在理解和使用它们时遇到麻烦,尤其是应用程序开发人员。 泛型最适合于库,框架和SDK。 在本文中,我将尝试与其他教程不同的方法。 我们将开设一家餐厅,并获得SwiftyCity市议会的许可。 为了诚实起见,我将尝试将事物归为四个主题。 泛型函数和泛型类型 关联类型的协议 通用条款 通用下标 开始了! 泛型函数和泛型类型 开一家斯威夫特餐厅 让我们建立一个新餐厅。 设置过程中,我们不仅会关注我们餐厅的建设,还将研究法律部分,并获得市议会的许可。 此外,我们将专注于我们自己的业务以使其运作并实现盈利。 首先,公司在理事会中的样子如何? 公司应具有一些基本功能。 protocol Company { func buy(product: Product, money: Money) func sell(product: Product.Type, money: Money) -> Product? } buy功能将产品添加到库存中,并从公司的金库中取出资金。 另一方面, sell函数获取产品类型,创建/查找产品并返回以换取金钱。 泛型函数 在此协议中, 实际类型的 Product听起来不正确。 不可能将每个实际产品都放入Product类型。 每个产品具有不同的功能,特性等。 在这类函数中使用实际类型确实是个坏主意。 让我们回到理事会。 在世界各地,无论每个公司做什么,都需要具有购买和出售的功能。 因此,理事会必须找到针对这两个职能的通用解决方案,并使它们适用于每个公司。 他们可以使用Generics改进这些功能: protocol Company […]

接口本地化

如果我们将应用定位到比我们的国家更广泛的范围,则最好对应用进行本地化以使用户易于理解。 一个很好的习惯是用您的母语(至少是英语)本地化应用程序。 在iOS应用程序中进行本地化可能有点棘手。 UI国际化 过去,本地化接口文件(xib或情节提要)时,xCode使用* language-code * .proj创建该文件的克隆。 我们具有完全相同的文件,可以在其中更改UI组件(如标签,按钮等)的属性。 这种方法的一个优点是,我们可以为不同的国家定义完全不同的UI。 基础国际化 为了避免克隆界面文件,我们可以在“项目常规”选项卡中标记“ 使用基础国际化”复选标记,而不是我们可以选择使用界面文件或字符串文件来国际化应用程序的UI部分。 / * Class =“ UIBarButtonItem”; title =“添加”; ObjectID =“ aFc-bV-NJn”; * / “ aFc-bV-NJn.title” =“添加”; / * Class =“ UINavigationItem”; title =“演示文稿”; ObjectID =“ cZ7-QZ-PGj”; * / “ cZ7-QZ-PGj.title” =“演示文稿”; 它减小了应用程序的大小。 我们尚未克隆我们支持的UI时代语言,但是此文件很难阅读/理解,甚至更难以维护。 当我们添加新的UILabel时,xCode不支持生命更新字符串文件。 使用代码本地化UI 当我们从代码构建整个视图时,很自然地从代码中设置本地化的字符串。 Wen我们有Xib或Storyboard文件,对每个控件进行输出并转换显示的值很奇怪。 我看到了一种解决方案,其中有人使用一种新颖的方法,该方法遍历视图层次结构中的所有子视图并根据标签或用户定义的运行时属性来翻译标签以查找翻译凯。 当我们添加新的本地化视图时,这会导致在viewDidLoad,awakeFromNib或其他初始化方法中调用一些代码……很多工作。 我的解决方案 在iOS应用程序中构建UI三年之后,我得出了一些结论,即如何构建易于维护和扩展的UI。 从一段时间以来,我几乎在Interface Builder中定义的每个组件都使用自定义类。 […]

与Objective-C Swift代码兼容

(本文最初是用俄语撰写的,并在此处发布。) 尽管Apple已向我们提供了有关如何在Objective-C应用程序中使用Swift代码的详细文档(反之亦然),但在此方面还远远不够。 当我需要为Swift框架提供与Objective-C的兼容性时,Apple文档提出了更多问题,然后给出了答案(或者至少留有很多空白)。 密集的搜索证明该主题的照明效果很差:有关StackOverflow的几个问题和一些介绍性文章–这就是我所发现的全部。 本出版物是对找到的信息和我自己的经验的概括。 所描述的所有方法并不能声称是一个很好的实践,它们只是提供了一种解决问题的方法。 TL; DR。 为了在Objective-C中使用Swift代码,必须牺牲一些Swift功能,并为原始的Swift代码编写一个包装程序,该代码将不使用不兼容的功能(例如结构,泛型,枚举关联值,协议扩展等)。 所有包装器类都必须继承NSObject。 开始 因此,我们有一个基于Objective-C的项目以及一些想要在其中使用的Swift代码模块。 例如,它可以是在CocoaPods的帮助下添加的Swift框架。 像往常一样,我们向Podfile添加依赖项,运行pod install ,打开xcworkspace文件。 要使Swift框架可见,就不需要使用整个模块(就像我们在Swift中所做的那样)或单个文件(就像我们在Objective-C中所做的那样)导入。 必须导入的是一个名为-Swift.h的文件-这是自动生成的头文件,该文件是Objective-C代码与Swift公共API之间的连接链接。 看起来像这样: #import“ YourProjectName-Swift.h” 在Objective-C中使用Swift类 如果您可以立即使用Objective-C中的某些Swift类或方法,那么您很幸运:有人为您解决了兼容性问题。 事实是,Objective-C仅消化NSObject继承者和属性的公共API,初始化程序和方法必须由@objc属性标记。 处理自己的代码时,您始终可以继承所需的任何内容并添加任何属性。 当然,在这种情况下,您甚至可以用Objective-C编写,对吗? 因此,最好专注于他人的代码。 我们可以做什么? 写包装纸。 例如,考虑以下Swift类: 公共类SwiftClass { 公共功能swiftMethod(){ //此处执行。 } } 我们创建自己的Swift文件,导入外部Swift模块,创建NSObject继承类,并在该类内部创建想要的Swift类型的私有属性。 最主要的是一个通过属性调用原始Swift类型方法的方法: 进口基金会 导入SwiftFramework 公共类SwiftClassObjCWrapper:NSObject { 私人让swiftClass = SwiftClass() @objc 公共功能swiftMethod(){ swiftClass.swiftMethod() } } ( NSObject和@objc属性都可以从Foundation获得。) 显然,我们不能使用相同的名称。 但是我们可以使用原始名称将包装器API公开给Objective-C: @objc(SwiftClass) […]