转自:http://blog.csdn.net/zhangqipu000/article/details/51965439
为什么要混编?
- 语言发展趋势(TIOBE),Swift 排行持续上升, OC 排行呈重力下降
- 项目正常迭代需要
- 很多第三方库仍然使用 OC 实现
- 项目中原来已经用 OC 实现的模块如果使用 Swift 重写,代价稍大
- 我们需要在项目中使用 Swift 才能真正碰到问题,解决问题
注:不是为了混编而混编。混编只是在对开发资源、项目管理和技术发展趋势进行综合衡量之后做出的比较合理的选择。
如何开始混编?
步骤
-
创建工程,Language 选择 Swift 或 Objective-C 都可以。
![这里写图片描述](https://img-blog.csdn.net/20150914112126388)
-
创建 Swift 文件并添加 bridging header 文件
![这里写图片描述](https://img-blog.csdn.net/20150914112354484)
添加 Swift 文件时 Xcode 会自动提示你添加 bridging header 文件,选择 Yes 即可
-
进行两处关键设置
![这里写图片描述](https://img-blog.csdn.net/20150914112530404)
这两处设置 Xcode 默认都会设置好,可以把 Objective-C Bridging Header 和 Objective-C Generated Interface Header Name 改成自己想设置的名字。
至此Swift 与 Objective-C 混编的环境就算配置完成了。
如果需要在 Swift 中使用 OC 的代码或者库,只需要在这个文件中 import
相应代码或者库的头文件即可。
XXX-Swift.h
和 XXX-Bridging-Header.h 不同,XXX-Swift.h 文件不会出现在项目中,而是由 Xcode 自动生成,你可以在类似如下的路径下找到相应项目的 XXX-Swift.h 文件:(PS:演讲时没有写到PPT里面,实在抱歉)
<code class="hljs lasso has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">/Users/perry/Library/Developer/Xcode/DerivedData/XXX<span class="hljs-attribute" style="box-sizing: border-box;">-bhlzdinkujybftbjmgwjwclndmss</span>/Build/Intermediates/XXX<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>build/Debug<span class="hljs-attribute" style="box-sizing: border-box;">-iphonesimulator</span>/XXX<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>build/Objects<span class="hljs-attribute" style="box-sizing: border-box;">-normal</span>/x86_64/XXX<span class="hljs-attribute" style="box-sizing: border-box;">-Swift</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>h</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>
如果需要在 OC 中使用 Swift 代码,在使用的文件中 #import XXX-Swift.h
(PS:其他一些在 OC 中使用 Swift 代码的注意事项会在后面详细说明)
查看 XXX-Swift.h 文件中的代码:
![这里写图片描述](https://img-blog.csdn.net/20150914145746705)
不难发现这个文件中的内容其实是将 Swift 中的代码转换成了 OC 的代码。
注:如果对项目进行清理操作,这个文件也会被删除,而且在重新构建的过程中,只有在所有的 Swift 代码都编译通过的情况下才会重新生成这个文件。
结合框架的混编
![这里写图片描述](https://img-blog.csdn.net/20150914150255348)
踩坑时间
OC 中的复杂宏
![这里写图片描述](https://img-blog.csdn.net/20150914150536939)
Swift 编译器不包含预处理器。取而代之的是,它充分利用了编译时属性,生成配置,和语言特性来完成相同的功能。所以对于上述类似的宏定义,建议用方法重新封装一次使用。
OC 中的宏和 Swift 中的类同名
因为 Swift 不能使用 #define
,而 OC 可以,所以你可能会在 OC 中定义一个和 Swift 中类同名字的宏,如果你从来没有在 OC 中使用 Swift 代码提供的功能(也就是从来没有 #import XXX-Swift.h
),编译时不会有任何问题,但是如果一旦使用了,就会报如下的错误:
![这里写图片描述](https://img-blog.csdn.net/20150914162741665)
这里是因为我使用 #define MView (@"MView")
将 MView
定义成了 NSString
类型的宏,而在 Swift 中 MView
是 UIView
的子类,在#import "MDemo-Swift.h"
之后, MView
就被重复定义了,从而导致错误。
更新:如果 OC 中的类和 Swift 中的类同名,也符合上述情况。
Swift 使用 OC 中的枚举
- 如果只是单纯使用值,可以直接使用枚举
- 如果需要对枚举值进行运算,则需要使用
.value
OC 使用 Swift 中的枚举
- 需要在 Swift 的枚举定义前加
@objc
修饰符 - 枚举的类型必须是
Int
@objc / dynamic / NS*
- 如果 Swift 类需要在 OC 中使用,建议继承 NS* 系列的类
- 如果 Swift 类中的成员或者方法需要在 OC 中使用,使用
@objc
修饰符 - 如果 Swift 类中的成员需要支持 KVC/KVO,使用
dynamic
修饰符
IBOutlet vs IBOutletCollection
Swift 中没有 IBOutletCollection
,而是如下的方式实现 IBOutletCollection
:
<code class="hljs scala has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@IBOutlet</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">var</span> labels: [UILabel]!</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>
这个 IBOutlet
在 xib/Storyboard 中的情况如下:
![这里写图片描述](https://img-blog.csdn.net/20150914165423892)
需要注意的是:和 OC 的 IBOutletCollection
不同,labels
中的元素不一定是按照 black,white,blue,green 的顺序排列!
重复包含
OC 中可能会碰到 A 类头文件需要包含 B 类头文件,B 类头文件同时也需要包含 A 类头文件的情况,这个时候用 @class
即可解决问题。但是当 OC 中的 A 类头文件需要包含 XXX-Swift.h
,而 XXX-Bridging-Header.h
中又 import
A类头文件,这个时候就比较尴尬了。看下面的例子:
![这里写图片描述](https://img-blog.csdn.net/20150915111708588)
![这里写图片描述](https://img-blog.csdn.net/20150915110640836)
![这里写图片描述](https://img-blog.csdn.net/20150915110700938)
上面例子的情况是这样的:
首先,我们有两个 Swift 类 MManager
和 MData
。 一个 OC 类 ViewController
。
然后,MManager
类中的一个方法需要传入 ViewController
类实例的句柄,然后把一个 MData
类实例赋值给 ViewController
类实例中的成员变量mData
(这个 mData
的类型是 MData
)。
在这种场景下,就会出现重复包含,因为已经有了 MManager
类中的一个方法需要传入 ViewController
类实例的句柄 这样一个条件,所以我们必须在XXX-Bridging-Header.h
中 #import "ViewController.h"
,这样我们就只能通过在 ViewController.m 文件中去 #import "XXX-Swift.h"
的方式来避免重复包含。此时我们需要将成员变量 mData
定义为 id
类型,当在ViewController.m 文件中具体用的时候再将其强制转换为 MData
类型。如下:
![这里写图片描述](https://img-blog.csdn.net/20150915130601282)
![这里写图片描述](https://img-blog.csdn.net/20150915130645076)
即可解决重复包含的问题。
这样的处理方式存在的问题是成员变量 mData
可以接受任何类型,所以我们在使用的时候要判断一下 mData
是否是我们需要的数据类型,这里我们介绍一下怎么判断 Swift 的 AnyObject
和 OC 的 id
是什么类型:
OC:
<code class="hljs php has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> ([<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span>.mData isKindOfClass:[MData <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span>]]) {</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 如果 self.mData 不是 MData 类型,判断不成功</span> }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>
Swift:
<code class="hljs haskell has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-title" style="box-sizing: border-box;">if</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">let</span> <span class="hljs-typedef" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">data</span> = self.mData as? <span class="hljs-type" style="box-sizing: border-box; color: rgb(102, 0, 102);">MData</span> <span class="hljs-container" style="box-sizing: border-box;">{ // 如果 <span class="hljs-title" style="box-sizing: border-box;">self</span>.<span class="hljs-title" style="box-sizing: border-box;">mData</span> 不是 <span class="hljs-type" style="box-sizing: border-box; color: rgb(102, 0, 102);">MData</span> 类型,判断不成功 }</span></span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>
properties
如果 OC 的类导入到 Swift 中使用,类的 properties 会有如下变化:
- Swift 中的属性都是
noatomic
的,所以 OC 类中的 atomic
将会失效 - OC 类中重写的
getter/setter
方法都将失效
Hello! Swift 2
var -> let
相比 Swift 1.2,Swift 2 强烈要求将在本方法体中值不会改变的量声明为常量,否则会出现 warning
,所以在编写 Swift 1.2 的代码时,可以提前注意这一点,从而减小转换成本。
println
Swift 2 中这个方法被删除,不要使用。
do/while -> repeat/while
因为有变化,所以建议用 for / for…in
代替
面向协议编程
从 Swift 2 开始,我们可以对协议进行扩展,从此正式开启了 Swift 的面向协议编程时代。因为目前还没有在项目中具体使用,所以研究不是很深,大家可以参考下面三篇译文:
Swift 2:面向协议编程
如何正确使用协议
Swift 2.0 中的面向协议的MVVM