Swift   发布时间:2022-03-31  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了将Swift与Objective-C相结合大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
@H_450_1@

概述

Objective-C起源于20世纪80年代初,尽管多年来这种语言有了长足的发展,却仍不敌Swift这样真正的现代化语言。随着Swift 3.0即将上线,使用Swift来编写新的应用会更加智能化。然而在PSPDFKit,我们仍坚守在Objective-C的世界里,我们建立、发布二进制框架以渲染/编辑PDF文件。想要正确获取所有的PDF细节是很复杂的,除了核心的PDF功能之外,我们还提供了大量可在典
@H_801_15@

将Swift与Objective-C相结合

@H_801_15@

@H_801_15@Objective-C起源于20世纪80年代初,尽管多年来这种语言有了长足的发展,却仍不敌Swift这样真正的现代化语言。随着Swift 3.0即将上线,使用Swift来编写新的应用会更加智能化。然而在PSPDFKit,我们仍坚守在Objective-C的世界里,我们建立、发布二进制框架以渲染/编辑PDF文件。想要正确获取所有的PDF细节是很复杂的,除了核心的PDF功能之外,我们还提供了大量可在典型用例中运用的UI类,从而产生了大约60万行的代码库,包含了UI和封装模型的混合代码——shared C++和Objective-C++都有,header部分完全是现代化的Objective-C,使用泛型和nullability注释,以确保在Swift中运行良好。

@H_801_15@

@H_801_15@尽管目前我们还处于Objective-C的世界中,但这种情况也并非全然糟糕的:通过一些精巧的设计,在类似我们这样的代码库中也甚至可以享用Swift的诸多好处。下面我们列出了一些将“新旧世界”结合起来的方法

@H_801_15@

为什么不单纯地使用Swift呢?
@H_801_15@

@H_801_15@我们先来谈谈这个十分明显却无人肯谈的问题。Swift是很棒的语言,有很多原因促使我们使用它;不过在很多场景和需求下,选择Objective-C则更为明智。具体选择哪种语言,要取决于应用及用例本身、你的团队还有项目的范围及本质。在这里苹果给了我们选择,这真是太棒了。

@H_801_15@

  • @H_801_15@Swift的发展速度快得不可思议。苹果的开放过程简直令人惊异,特别要虑到这家公司谨言慎行的本质。尽管将Swift最初发布的版本称为1.0版尚且有些草率,不过很快它就发展成了一种快速、安全、可以编写出优雅代码的语言。对于早期的采用者来说,还有很多甚至称得上严重的bug和问题需要解决。对于较小的项目或者典型应用来说,Swift可能运行良好,但大型项目可能会因为编译时间、优化问题或者仅仅缺乏资源来停止开发并花上数周更新数据库到Swift 3——这项任务可能会带来极大的破坏性——而推迟采用Swift。

@H_801_15@

  • @H_801_15@当前的Swift在很多方面与C++很相似,都属于非常静态的类型,在动态消息发送和运行环境方面比不上Objective-C。在过去,这一点然导致了很严重的问题出现,比如优化问题或者不应使用的monkey-patching代码,但也带来了许多优雅的解决方案,比如包括NsmanagedObject、NSUndoManager、UIAppearance等Core Data对象的动态特性解析,还有很多其它苹果框架中我们所喜爱的功能这个问题很难平衡,即便是苹果UIKit团队的员工也要谨慎对待这种危险。

@H_801_15@

  • @H_801_15@使用不具有二进制兼容性的Swift语言意味着我们必须对用户关闭一些技术参数,同时他们在选择Xcode时也会有局限——如果我们的SDK仍旧使用Xcode 7.3.0编译,他们也许就不能升级到Xcode 7.3.1。每个极小的编译器版本更改都可能会导致代码与其他版本不能兼容,而我们并不想让用户烦恼这种额外的技术复杂性。我们明白自己是极端型案例,对大多项目来说并不会有太大问题。我们也坚信,推迟采用并等待稳定的Swift ABI是件好事,尽管短期内不够方便,但长期来讲我们所使用的语言更完善。同时,我们的用户也很在意数据包的大小,可能会介意采用Swift所造成的每个架构6MB的额外负担。由于我们一直支持最近两版的iOS,也就是说至少在最近两年内我们都无法改用Swift。

@H_801_15@

@H_801_15@在编写测试和样例代码时,我们越来越多地使用Swift,也非常喜欢它。但同时,我们也担心Xcode 8的转变与额外的复杂性会对团队产生负担。由于ABI还在变化,我们无法在主要的SDK中使用Swift。因此我们决定采用Objective-C++在恰当的地方对纯Objective-C进行补充。

@H_801_15@

@H_801_15@这种做法看似非常复杂,可能会让很多人产生担心:在代码中加入C++这种可能非常复杂的语言——难以学习甚至难以驾驭,也许会花费很多的时间和精力,但实际上并非如此。与其将Objective-C++当作Objective-C加C++,不如把它当成Objective-C的一个小语种。我们在Objective-C类中仅使用了极少量的C++,以借助C++的便利、安全性与性能方面的优势。这与实现完全成熟的C++不同,在以Objective-C为主的代码库中尝试使用一个小的子集是非常简单的,即便对于没有C++经验的开发者来说也是如此。

@H_801_15@

Objective-C++入门
@H_801_15@

@H_801_15@我们先来看一下在项目中使用Objective-C++所需的步骤,假设已有以Objective-C编写的项目:

@H_801_15@

  1. @H_801_15@将想要使用Objective-C++的文件重命名,从<MyClass>.m改为<MyClass>.mm;

  2. @H_801_15@完成,就是这样,不需要步骤二。

@H_801_15@

@H_801_15@真的非常简单,Objective-C与C++具有高度的协作性,因此无需安装任何内容,也完全不用修改设置。当然,也并非所有的C代码都是有效的C++代码,有时可能需添加一些额外的转换,不过大部分情况都是没问题的。Xcode 7并不支持Objective-C++中的模块,因此必须使用较旧的#import语法,而不是新的@import。

@H_801_15@

@H_801_15@现在我们知道,在应用中支持Objective-C++实际上非常简单,来看一下能用它做些什么。下面是我们最喜欢的一些功能

@H_801_15@

@H_801_15@auto

@H_801_15@

@H_801_15@看下这段代码

@H_801_15@

@H_801_15@

@H_801_15@这实际上是我们测试中的一个bug,在打算将文件名列表作为字符串时,其中有文件包含NSURL对象。最终由于相关文件自动过滤掉,测试过了,有一阵子没人注意日志记录。如果我们使用Objective-C的新泛型功能(苹果专为Swift 2添加),编译器就会捕捉到这个问题:

@H_801_15@

@H_801_15@

将Swift与Objective-C相结合

@H_801_15@

@H_801_15@

@H_801_15@在使用泛型时,区分符的输入非常烦人:

@H_801_15@

@H_801_15@

@H_801_15@现在一下子就能解决了,我们可以简化它,同时保持C++正确的模板参数:

@H_801_15@

@H_801_15@

@H_801_15@好多了,allAnnotationsDict是什么仍然很明显。在编译时,auto功能可以像上面这样自动完成,无需配置运行环境。Swift编译器团队的Joe Groff指出:目前在标准C和ObjC中,top-of-tree clang支持__auto_type类型推论了,因此我们终于可以在无需C++编译开销的情况使用它。

@H_801_15@

@H_801_15@内联块

@H_801_15@

@H_801_15@虑一下使用内联块处理注释的情况,由于需要三个参数,声明的长度几乎让人难以忍受。通常我们会将它改成辅助函数,不过由于无法捕获变量,结果可能会让情况更加复杂。

@H_801_15@

@H_801_15@

@H_801_15@这个声明还有个问题,就是非常冗长,每个参数类型都要写两遍,开发者通常都很厌恶冗长,因此我们来做些清理。auto再次成了救星:

@H_801_15@

@H_801_15@

@H_801_15@let

@H_801_15@

@H_801_15@Swift的优点之一在于:在声明变量时,let是使用最多也最方便的办法,会自动产生const。同时在C语言中也有const,只不过非常丑陋:

@H_801_15@

@H_801_15@

@H_801_15@有了auto,可以写成可读性更高的样子:

@H_801_15@

@H_801_15@

@H_801_15@甚至可以更疯狂——使用一个宏:

@H_801_15@

@H_801_15@

@H_801_15@vector

@H_801_15@

@H_801_15@在Swift中,我们可以将任何数据类型放在数组中:

@H_801_15@

@H_801_15@

@H_801_15@在Objective-C中,NSArray只能包含对象,不但更为复杂,同时由于封装的问题,对于基本类型的处理速度也更慢。当然,我们可以使用C数组,但会使得添加移除元素或另存数组之类的通用操作更为复杂,可能需要手动执行内存管理与@L_357_55@malloc()。有了Objective-C++,我们可以简单地使用std::vector:

@H_801_15@

@H_801_15@

@H_801_15@无论显式struct字段命名,还是较短的隐式版本{0,0}都是可用的,由于vector<CGPoint>已知想要的数据类型,无需再编写(CGPoint)转换。此外对C++容器const之后,它们也会自动成为不可变量。

@H_801_15@

@H_801_15@vector <-> NSArray

@H_801_15@

@H_801_15@有时候会出现需要将vector转化为NSArray的情况,反过来也是一样。这种操作非常简单,但如果使用Helper会更好。

@H_801_15@

@H_801_15@

@H_801_15@运算符重载

@H_801_15@

@H_801_15@大家是否有时候需要计算CGRect、CGSize或Core Graphics的其他几何类型呢?它们都是struct,然好处很多,但计算时非常烦人,这里再次出现了冗余代码

@H_801_15@

@H_801_15@

@H_801_15@在Swift中,定义运算符非常简单,从而使得这些操作也很简单,但在Objective-C++中我们也能这样做:

@H_801_15@

@H_801_15@

@H_801_15@锁定(Locks)

@H_801_15@

@H_801_15@在构建线程安全API时,需要锁定。在标准Objective-C中,可以像下面这样做:

@H_801_15@

@H_801_15@

@H_801_15@代码很多,但只描述了一个代码应当执行的状态。在Objective-C++中,我们可以采用更简单的办法:

@H_801_15@

@H_801_15@

@H_801_15@在超出范围后,C++会自动锁定,在C++中,到处都是资源分配即初始化(RAII)模式,它也确实很好用,允许我们通过返回语句来执行需要内联锁定的操作,因为锁定只会在返回后自动解锁。

@H_801_15@

@H_801_15@如果我们只需要锁定某个method的很小一部分,就可以简单地创建一个较小的范围来执行:

@H_801_15@

@H_801_15@

@H_801_15@如果需要递归锁,可以使用std::recursive_mutex来代替std::mutex。

@H_801_15@

@H_801_15@可选方案:有一个简单的纯Objective-C解决方案,生成@L_33_25@method,在锁定时执行一个块参数,比如[NsmanagedObjectContext performBlock:]。

@H_801_15@

@H_801_15@模板

@H_801_15@

@H_801_15@有时候模板在避免重复代码方面非常有效,试想一下负责比较类似CGFloat或NSInteger的Helper,我们随时可以将其封装并调用compare:,但开销很大。更好的办法是使用模板函数

@H_801_15@

@H_801_15@

@H_801_15@一个很有用的Helper是条件转换——查看某个类是否是正确的类型。

@H_801_15@

@H_801_15@

@H_801_15@在if中的变量声明

@H_801_15@

@H_801_15@在Swift中,典型用法就是在if-else块区中声明变量:

@H_801_15@

@H_801_15@

@H_801_15@在Objective-C++也可以采用类似的做法:

@H_801_15@

@H_801_15@

STL算法
@H_801_15@

@H_801_15@标准的模板库中有很多有用的算法,这里不列举代码片段,请参Sean Parent的演讲《C++ Seasoning》,对拓展思维很有好处。

@H_801_15@

问题和缺点
@H_801_15@

@H_801_15@这些简单的调整有什么缺点呢?我们不想说谎——缺点确实有一些,但我们认为到目前为止使用它们的优势更大。

@H_801_15@

@H_801_15@编译时间

@H_801_15@

@H_801_15@文件扩展名从.m修改为.mm之后,clang将开始从C++的角度评估文件,而在自动转换方面C++更为严格。因此有时在使用中会收到一些警告,特别当代码中包含类似MAX()这样的宏时。在std::max()的情况下,可以通过显式转换或者使用C++函数的替代来解决这些问题。如果出现问题,或者有时Objective-C在类型上太松懈,就必须自行确定该如何处理。

@H_801_15@

@H_801_15@编译.mm文件比标准的.m文件花费的时间更长一些,不过凭我们的经验来看,这点代价是值得的。如果你在使用一个大型代码库的话,额外时间累积起来可能会很多,但使用一些额外的编译缓存能够抵消很多消耗掉的时间。大量使用模板或者用到模板的库会产生更大的影响。

@H_801_15@

@H_801_15@工具

@H_801_15@

@H_801_15@一个风险在于:很多人广泛使用Objective-C++,因此很可能会遇到编译错误或边缘情况。目前我们只遇到过一个Clang Analyzer崩溃的问题,不过在纯Objective-C代码中我们也曾设法重现过这个问题。

@H_801_15@

@H_801_15@在header中避免使用C++

@H_801_15@

@H_801_15@如果非要添加的话,将它或者放在单独的.hpp header中,或者放在#if __cplusplus后面;否则很快你就必须将整个项目转换为.mm格式,而且header对Swift来说不可访问。

@H_801_15@

@H_801_15@意外副本

@H_801_15@

@H_801_15@C++喜欢复制内容,看看这段代码

@H_801_15@

@H_801_15@

@H_801_15@这段代码没有任何作用,属性会返回一个修改后的向量副本,调用后自毁。有很多办法来解决这个问题,使用共享指针就是办法之一:

@H_801_15@

@H_801_15@

@H_801_15@C++11加入了unique_ptr、shared_ptr和weak_ptr,它们在很多方面与ARC类似,只是速度更快也更有确定性,因为其中没有自动释放池。共享指针很好用,在大多情况下调用new或者delete命令属于糟糕的设计,可以使用智能指针来替代。

@H_801_15@

Objective-C的功能
@H_801_15@

@H_801_15@我们使用了大量并非人尽皆知的Objective-C功能,还有大量可用的辅助函数代码更具有可读性,尤其是在处理集合时。

@H_801_15@

@H_801_15@NS_NOESCAPE

@H_801_15@

@H_801_15@在Swift中,@noescape声明允许编译器在block内优化代码。尽管我们没有NS_NOESCAPE,也可以使用下面的办法:

@H_801_15@

@H_801_15@

@H_801_15@当然我们提交了rdar://25737301,另有一个Swift proposal建议将其添加到Objective-C中,希望很快能看到这样的变化。

@H_801_15@

@H_801_15@点语法

@H_801_15@

@H_801_15@这是一个有争议的话题,我们在任何没有负面作用的方法中使用点语法——即便没有声明为属性

@H_801_15@

@H_801_15@

@H_801_15@在iOS 7的SDK中,苹果将很多应当是属性方法都转换成了属性功能上并无区别,只是现在能更好地执自动补全了。这里的缺点在于,Xcode无法自动补全点语法调用方法

@H_801_15@

@H_801_15@@H_53_26@map,filter,flatMap

@H_801_15@

@H_801_15@类似NSArray和NSSet这样的数据结构缺少高阶函数。一些有用的方法长度夸张,使用起来也很不方便

@H_801_15@

@H_801_15@看下这段从页面访问中收集选择注释的代码

@H_801_15@

@H_801_15@

@H_801_15@使用我们的flatMap助手来编辑相同的代码

@H_801_15@

@H_801_15@

@H_801_15@整个Helper非常简单,还有不同的变体可以返回一个进行更好链接的block,我们选择了更为Objective-C风格的API,在数组为空的情况下不会崩溃:

@H_801_15@

@H_801_15@

@H_801_15@我们有类似方法可用于filter或map,以及一系列类似-[NSArray pspdf_mutatedArrayUsingBlock:]的Helper可封装大量每个人都写了数百次的样板代码。尽管我们的Helper目前还没有开源,但有不少有用的开源项目。BlocksKit在上述实现方面表现十分优秀

@H_801_15@

结论
PSPDFKit中,我们平时会使用本文中提到的方法,并且确信这些方法不但会使我们的代码可读性更高,同时也增加代码库的安全性,另外,由于无需再重复编写相同的样板代码(在Objective-C开发中太过常见的一些代码),其中的很多方法也加快了开发速度。有很多其他的应用与框架也使用Objective-C++,Realm CocoaPaper by FiftyThreeRxPromiseDropBox DjinniFacebook的ComponentKit还有Pop——甚至很多苹果的框架,比如Core Graphics、WebKit/WKWebView甚至Objective-C runtime都有运用到Objective-C++。@H_801_15@ @H_801_15@

@H_801_15@

@H_801_15@

@H_801_15@第一时间掌握最新移动开发相关信息和技术,请关注mobilehub公众微信号(ID: mobilehub)。

@H_801_15@

将Swift与Objective-C相结合

大佬总结

以上是大佬教程为你收集整理的将Swift与Objective-C相结合全部内容,希望文章能够帮你解决将Swift与Objective-C相结合所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。