Swift与Objective-C混和编程

14年6月3日苹果发布Swift以来,这门语言以让人惊讶的速度在成长,越来越多的开发者关注学习,很多App和开源库也在从Objective-C迁移到Swift上。

Swift语法确实更新进、更漂亮,而在实际开发过程中,由于Objective-C更贴近底层,可以使用如OC Runtime这样的黑魔法,很多开源库也是依赖其实现。因此OC和Swift混编应该一个长期的趋势,之前只是依赖于Xcode自动引入bridge header等类似的机制,没有仔细去理解,借着新做项目用到evernote oc库的机会好好的总结一下。苹果提供的Swift与Objective-C混编方案都是基于Xcode和LLVM编译,采用Mix and Match机制。

Swift与Objective-C混和编程_第1张图片

从开发者实现角度根据不同的混编场景可以分为如下几种情况:

  • 普通代码混编:项目内普通代码文件混编(.swift内使用OC的.h和.m文件或者反过来,包括.a形式项目的开发),采用的bridge方案;

  • 开发Framework混编:如果你的项目是输出一个Framework,混编方式稍有不同,姑且成为umbrella方案;

  • 引用外部Framework和宿主App混编:如果你的项目引用一个外部提供的Framework(无论这个Framework是单一语言开发还是本身就是混编的),混编方案也有不同。> 详细的原理参见上文提到的官方文档,本文主要关注三种方式的实现以及可能遇到的问题。

普通代码文件混编方案:

Swift与Objective-C混和编程_第2张图片

Swift引用OC实现通过桥接头文件,OC引用Swift实现直接importProductModuleName-Swift.h这个文件即可。

OC引用Swift实现

ProductModuleNameBuild Settings里面配置:

Swift与Objective-C混和编程_第3张图片

默认用ProductName,可以支持自定义。(注明:Framework项目不支持自定义)

Swift引用OC实现

Swift引用OC实现稍微麻烦一点,需要自己生成一个bridge header文件,和创建普通.h方式相同File > New > File > (iOS, watchOS, tvOS, or OS X) > Source > Header File,名字随意,然后配置到Build Settings - Swift Compiler - Code Generation下的Objective-C Bridging Header选项。

Swift与Objective-C混和编程_第4张图片

注意路径从项目根目录开始计算,可以使用..来指定与根目录平级目录。bridge header内import所有想要在swift中使用的OC类,就会作为一个module在swift中使用。例如:

#import "XYZCustomCell.h"    

#import "XYZCustomView.h"   

#import "XYZCustomViewController.h"

Swift中用如下代码访问:

let myOtherCell = XYZCustomCell()    

myOtherCell.subtitle = "Another custom cell"

FYI. 语言类型为Swift的项目引入OC文件时Xcode会给个创建bridge header的提示,自己会配置了之后用处不大:

Swift与Objective-C混和编程_第5张图片

Framework项目中使用代码混编方案:

Swift与Objective-C混和编程_第6张图片

Umbrella Header的相关知识苹果没有给出很明确的说明,只有以前介绍Umbrella Framework的时候介绍过,找了很久发现iOS - Umbrella Header在framework中的应用这篇文章介绍的很好,详细的内容可以进入了解。

Swift引用OC实现

现在我们只需要了解Framework里面Swift引用OC逻辑需要一个与ProductName同名的.h文件作为Umbrella Header,如果不存在则创建一个。不需要在Build Settings配置因为这文件是map modules的时候自动指定的,如果基于某种原因(比如这个同名文件已经被用来写其他逻辑)一定要自定义的话可以参考上面文章里介绍的方法。第二步到Build Settings - Packaging中将Defines Module选项设为YES。然后将Swift中需要引用的OC逻辑引用进来,访问方式同普通代码混编

#import "XYZCustomCell.h"    

#import "XYZCustomView.h"    

#import "XYZCustomViewController.h"

OC引用Swift

实现OC引用Swift同样需要将Defines Module选项设为YES,其余和普通代码混编相比只是改了个引用文件的方式:#import

引用外部Framework时混编

方案:

重要前提

这里有一个重要的前提是这个外部Framework在编译时必须开启了Defines Module,如果没有开启并且没有Framework源码的情况下还是绕路吧。

external framework混编

在这种情况下当前App使用外部Framework是不关心其内部到底是Swift实现、OC实现还是本身就是混编实现的。只需要Swift使用Framework逻辑时添加import FrameworkName,OC使用时在任意.m文件中添加@import FrameworkName;语法即可。

混编后哪些逻辑可以被另一种语言引用到?

Swift中可以被OC引用的逻辑:

  • public关键字;

  • 有bridging header的target中用internal关键字修饰;

  • private修饰的关键字通常是访问不到的,除了@IBAction, @IBOutlet, 和 @objc标记;

OC中由于开发习惯的原因基本上头文件中的属性、方法都可以被swift访问到。

Evenote-Mac Framework混编时遇到的问题

  1. Evenote-Mac这个奇葩的Framework名字在生成umbrella header的时候报错:

warning: EvernoteSDK-Mac is not a valid PRODUCT_NAME for use with framework targets enabling DEFINES_MODULE (name is not a valid C99 extended identifier)

warning: no umbrella header found for target 'EvernoteSDK-Mac', module map will not be generated

因为名字中有-字符,所以只能替换或者去掉;

  1. 改名时建议直接改target的名字,只改module的名字就会报错:

Warning: PRODUCT_MODULE_NAME may not be overridden for framework target 'EvernoteSDKMac'

参考链接

  1. Mixed language framework

  2. 链接1回答中还有个Demo

  3. 官方对Umbrella Framework的一点介绍

  4. 对Umbrella Framework的一篇更好的介绍

  5. so上有关framework name的回答

你可能感兴趣的:(Swift与Objective-C混和编程)