美文网首页深度编程
Swift与Objective-C混和编程

Swift与Objective-C混和编程

作者: kimimaro | 来源:发表于2015-11-29 13:48 被阅读1984次

    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内使用OC的.h和.m文件或者反过来,包括.a形式项目的开发),采用的bridge方案;

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

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

    普通代码文件混编方案:

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

    OC引用Swift实现

    ProductModuleNameBuild Settings里面配置:

    默认用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选项。

    注意路径从项目根目录开始计算,可以使用..来指定与根目录平级目录。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的提示,自己会配置了之后用处不大:

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

    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的回答

    相关文章

      网友评论

      • zolobdz:大佬看到这是很早之前的文章了,有一点不明白:1.Define Moudle到底是做什么的?上面只说了使用场景。2.如果是swift项目中又创建了oc文件,那么oc文件应该如何访问swift文件?还是说这种情况oc基本不能访问swift文件(除了使用NSStringFromClass)

      本文标题:Swift与Objective-C混和编程

      本文链接:https://www.haomeiwen.com/subject/xxfjhttx.html