swift 与 OC 混合编程
原文地址:http://www.cocoachina.com/swift/20150608/12025.html
一、解决问题
Swift项目需要使用封装好的Objective-c组件、第三方类库,苹果提供的解决方案能够处理日常大部分需求,但还不能称之为完美,混编过程中会遇到很多问题。本文将Swift兼容Objective-c的问题汇总,以帮助大家更好的使用Swift,内容列表如下:
1. Swift调用Objective-c代码
2. Objective-c调用Swift代码
3. Swift兼容Xib/Storyboard
4. Objective-c巧妙调用不兼容的Swift方法
5. 多Target编译错误解决
6. 第三方类库支持
二、基础混合编程
Swift与Objective-c的代码相互调用,并不像Objective-c与C/C++那样方便,需要做一些额外的配置工作。无论是Swift调用Objective-c还是Objective-c调用Swift,Xcode在处理上都需要两个步骤:
2.1 Swift调用Objective-c代码
Xcode对于Swift调用Objective-c代码,除宏定义外,其它支持相对完善。
2.1.1 使用Objetvie-c的第一步
告诉Xcode、哪些Objective-c类要使用,新建.h头文件,文件名可以任意取,建议采用“项目名-Bridging-Header.h”命令格式。
Tips
Swift之IOS项目,在Xcode6创建类文件,默认会自动选择OS X标签下的文件,这时一定要选择iOS标签下的文件,否则会出现语法智能提示不起作用,严重时会导致打包出错。
2.1.2 第二步,Target配置,使创建的头文件生效
设置Objective-C Bridging Header时,路径要配置正确,例如:创建的名为“ILSwift-Bridging-Header.h”文件,存于ILSwift项目文件夹的根目录下,写法如下:
1
|
ILSwift/ILSwift-Bridging-Header.h |
当然,在新项目中,直接创建一个Objective-c类,Xcode会提示:
直接选择Yes即可,如果不小心点了其它按钮,可以按照上面的步骤一步一步添加。
2.2 Objective-c调用Swift代码
2.2.1 Objective-c调用Swift代码两个步骤
第一步告诉Xcode哪些类需要使用(继承自NSObject的类自动处理,不需要此步骤),通过关键字@objc(className)来标记
1
2
3
4
5
6
7
8
|
import UIKit @objc(ILWriteBySwift) class ILWriteBySwift { var name: String! class func newInstance() -> ILWriteBySwift { return ILWriteBySwift() } } |
第二步引入头文件,Xcode头文件的命名规则为
1
|
$(SWIFT_MODULE_NAME)-Swift.h |
示例如下:
1
|
#import "ILSwift-Swift.h" |
Tips
不清楚SWIFT_MODULE_NAME可通过以下步骤查看
2.2.2找不到$(SWIFT_MODULE_NAME)-Swift.h
1.遇到此问题可按以下步骤做常规性检查
确定导入SWIFT_MODULE_NAME)-Swift.h头文件的文件名正确
SWIFT_MODULE_NAME)-Swift.h在clean后没有重新构建,执行Xcode->Product->Build
2.头文件循环
在混合编程的项目中,由于两种语言的同时使用,经常会出现以下需求:在Swift项目中需要使用Objectvie-c写的A类,而A类又会用到Swift的一些功能,头文件的循环,导致编译器不能正确构建$(SWIFT_MODULE_NAME)-Swift.h,遇到此问题时,在.h文件做如下处理
1
2
3
4
|
//删除以下头文件 //#import "ILSwift-Swift.h" //通过代码导入类 @class ILSwiftBean; |
在Objevtive-c的.m文件最上面,添加
1
|
#import "ILSwift-Swift.h" |
出现Use of undecalared identifier错误或者找不到方法,如下:
引起的原因有以下几种可能:
使用的Swift类不是继承自NSObject,加入关键字即可
SWIFT_MODULE_NAME)-Swift.h没有实时更新,Xcode->Product->Build
此Swift文件中使用了Objective-c不支持的类型或者语法,如private
出现部分方法找不到的问题,Xcode无智能提示:
此方法使用了Objective-c不支持的类型或者语法
苹果官方给出的不支持转换的类型
Generics
Tuples
Enumerations defined in Swift
Structures defined in Swift
Top-level functions defined in Swift
Global variables defined in Swift
Typealiases defined in Swift
Swift-style variadics
Nested types
Curried functions
三、Xib/StoryBoard支持
Swift项目在使用Xib/StoryBoard时,会遇到两种不同的问题
Xib:不加载视图内容
Storyboard:找不到类文件
3.1 Xib不加载视图内容
在创建UIViewController时,默认选中Xib文件,在Xib与类文件名一致时,可通过以下代码实例化:
1
|
let controller = ILViewController() |
运行,界面上空无一物,Xib没有被加载。解决办法,在类的前面加上@objc(类名),例如:
1
2
3
4
|
import UIKit @objc(ILViewController) class ILViewController: UIViewController { } |
Tips:
StoryBoard中创建的UIViewController,不需要@objc(类名)也能够保持兼容
3.2 Storyboard找不到类文件
Swift语言引入了Module概念,在通过关键字@objc(类名)做转换的时候,由于Storboard没有及时更新Module属性,会导致如下两种类型错误:
3.2.1 用@objc(类名)标记的Swift类或者Objective-c类可能出现错误:
2015-06-02 11:27:42.626 ILSwift[2431:379047] Unknown class _TtC7ILSwift33ILNotFindSwiftTagByObjcController in Interface Builder file.
解决办法,按下图,选中Module中的空白,直接回车
3.2.2 无@objc(类名)标记的Swift类
1
|
2015-06-02 11:36:29.788 ILSwift[2719:417490] Unknown class ILNotFindSwiftController in Interface Builder file. |
解决办法,按下图,选择正确的Module
3.产生上面错误的原因: 在设置好Storyboard后,直接在类文件中,添加或者删除@objc(类名)关键字,导致Storyboard中 Module属性没有自动更新,所以一个更通用的解决办法是,让Storyboard自动更新Module,如下:
3.3 错误模拟Demo下载
为了能够让大家更清楚的了解解决流程,将上面的错误进行了模拟,想动手尝试解决以上问题的同学可以直接下载demo
四、Objective-c巧妙调用不兼容的Swift方法
在Objective-c中调用Swift类中的方法时,由于部分Swift语法不支持转换,会遇到无法找到对应方法的情况,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import UIKit enum HTTPState { case Succed, Failed, NetworkError, ServerError, Others } class ILHTTPRequest: NSObject { class func requestLogin(userName: String, password: String, callback: (state: HTTPState) -> (Void)) { dispatch_async(dispatch_get_global_queue(0, 0), { () -> Void in NSThread.sleepForTimeInterval(3) dispatch_async(dispatch_get_main_queue(), { () -> Void in callback(state: HTTPState.Succed) }) }) } } |
对应的$(SWIFT_MODULE_NAME)-Swift.h文件为:
1
2
3
4
|
SWIFT_CLASS( "_TtC12ILSwiftTests13ILHTTPRequest" ) @interface ILHTTPRequest : NSObject - (SWIFT_NULLABILITY(nonnull) instancetype)init OBJC_DESIGNATED_INITIALIZER; @end |
从上面的头文件中可以看出,方法requestLogin使用了不支持的Swift枚举,转换时方法被自动忽略掉,有以下两种办法,可以巧妙解决类似问题:
4.1 用支持的Swift语法包装
在Swift文件中,添加一个可兼容包装方法wrapRequestLogin,注意此方法中不能使用不兼容的类型或者语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import UIKit enum HTTPState: Int { case Succed = 0, Failed = 1, NetworkError = 2, ServerError = 3, Others = 4 } class ILHTTPRequest: NSObject { class func requestLogin(userName: String, password: String, callback: (state: HTTPState) -> (Void)) { dispatch_async(dispatch_get_global_queue(0, 0), { () -> Void in NSThread.sleepForTimeInterval(3) dispatch_async(dispatch_get_main_queue(), { () -> Void in callback(state: HTTPState.Succed) }) }) } class func wrapRequestLogin(userName: String, password: String, callback: (state: Int) -> (Void)) { self.requestLogin(userName, password: password) { (state) -> (Void) in callback(state: state.rawValue) } } } |
对应的$(SWIFT_MODULE_NAME)-Swift.h文件为:
1
2
3
4
5
|
SWIFT_CLASS( "_TtC12ILSwiftTests13ILHTTPRequest" ) @interface ILHTTPRequest : NSObject + (void)wrapRequestLogin:(NSString * __nonnull)userName password:(NSString * __nonnull)password callback:(void (^ __nonnull)(NSInteger))callback; - (SWIFT_NULLABILITY(nonnull) instancetype)init OBJC_DESIGNATED_INITIALIZER; @end |
此时,我们可以在Objective-c中直接使用包装后的方法wrapRequestLogin
4.2 巧妙使用继承
使用继承可以支持所有的Swift类型,主要的功能在Objective-c中实现,不支持的语法在Swift文件中调用,例如,ILLoginSuperController做为父类
1
2
3
4
5
6
7
8
9
10
11
|
@interface ILLoginSuperController : UIViewController @property (weak, nonatomic) IBOutlet UITextField *userNameField; @property (weak, nonatomic) IBOutlet UITextField *passwordField; - (IBAction)loginButtonPressed:(id)sender; @end //////////////////////////////////////////////////////////////// @implementation ILLoginSuperController - (IBAction)loginButtonPressed:(id)sender { } @end |
创建Swift文件,继承自ILLoginSuperController,在此Swift文件中调用那些不支持的语法
1
2
3
4
5
6
7
8
|
import UIKit class ILLoginController: ILLoginSuperController { override func loginButtonPressed(sender: AnyObject!) { ILHTTPRequest.requestLogin(self.userNameField.text, password: self.passwordField.text) { (state) -> (Void) in //具体业务逻辑 } } } |
五、多Target编译错误解决
在使用多Target时,会出现一些编译错误
5.1 Use of undeclared type
此类错误,是因为当前运行的Target找不到必须编译文件。将文件添加到Target即可,如下支持ILSwiftTests Target,选中ILSwiftTests前的复选框即可
5.2 does not have a member named
此类错误可能由于如下两种原因引起,解决办法同上:
1.此方法来自父类,父类文件没有加入到当前Target
2.此方法来自扩展,扩展没有加入到当前Target
Tips
如果检查发现,所有的类文件都已经准确添加到Target中,但编译还是不通过,此时着重检查桥接文件是否正确设置,是否将相应的头文件加入到了桥接文件中。如无特别要求,建议将所有Target的桥接文件全都指向同一文件。关于桥接文件的设置,请参考2.1
六、第三方类库支持
Swift项目取消了预编译文件,一些第三方Objective-c库没有导入必要框架(如UIKit)引起编译错误
6.1 Cocoapods找不到.o文件
在使用了Cocoapods项目中,会出现部分类库的.o文件找不到,导致此种错误主要是以下两种问题:
类库本身存在编译错误
Swift没有预编译,UIKit等没有导入
将此库文件中的代码文件直接加到项目中,编译,解决错误。
6.2 JSONModel支持
在Swift中可以使用JSONModel部分简单功能,一些复杂的数据模型建议使用Objevtive-c
1
2
3
4
5
6
7
|
import UIKit @objc(ILLoginBean) public class ILLoginBean: JSONModel { var userAvatarURL: NSString? var userPhone: NSString! var uid: NSString! } |
Tips
在Swift使用JSONModel框架时,字段只能是NSFoundation中的支持类型,Swift下新添加的String、Int、Array等都不能使用
6.3 友盟统计
Swift项目中引入友盟统计SDK会出现referenced from错误:
解决办法,找到Other Linker Flags,添加-lz
七、综述
现在大部分成熟的第三方框架都是使用Objective-c写的,开发时不可避免的涉及到两种语言的混合编程,期间会遇到很多奇怪的问题。因为未知才有探索的价值,Swift的简洁快速,能够极大的推进开发进度。所以从今天开始,大胆的开始尝试。
swift 与 OC 混合编程的更多相关文章
- Swift和Objective-C混合编程——Swift调用OC
在iOS应用的开发中.Swift必将代替OC,两者的趋势是"短期共存,长期代替".但曾经有太多的代码是用OC语言完毕的,而Swift的开发也从 OC中继承了非常多的特性.两者也是有 ...
- Swift与OC混合开发
一.Swift调用OC 1. 创建{targetName}-Bridging-Header.h头文件,在BuildSetting -> bridging 2. Swift文件调用的OC中的类的头 ...
- Swift 与 Objective-C混合编程
在Swift项目中想要同一时候加入Objective-C的库支持或者须要同一时候用Objective-C编程 在加入新的文件时选择Objective-C系统就会自己主动生成一个xx-Bridging- ...
- Swift和Objective-C混合编程
假设你现在就是一个iOS程序员,你对Objective-C很熟悉,对iOS开发也很熟悉,然而,苹果公司在iOS 8之后推出了Swift语言.那么,如何才能快速地从Objective-C过渡到Swift ...
- 《从零开始学Swift》学习笔记(Day 69)——Swift与Objective-C混合编程之语言
原创文章,欢迎转载.转载请注明:关东升的博客 在Swift语言出现之前,开发iOS或OS X应用主要使用Objective-C语言,此外还可以使用C和C++语言,但是UI部分只能使用Objective ...
- IOS-Swift、Objective-C、C++混合编程
1.Objective-C调用C++代码 后缀为m文件的是Objective-C的执行文件,而后缀为mm文件的是Objective-C++文件. 直接在Objective-C中是无法调用C++代码的, ...
- Objective-C与Swift的混合编程
Swift 被设计用来无缝兼容 Cocoa 和 Objective-C .在 Swift 中,你可以使用 Objective-C 的 API(包括系统框架和你自定义的代码),你也可以在 Objecti ...
- Objective-C:swift、objective-c、C++、C混合编程
@import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css); @import url(/ ...
- Swift 与 C 语言混合编程
前言 作为一种可与 Objective-C 相互调用的语言,Swift 也具有一些与 C 语言的类型和特性,如果你的代码有需要,Swift 也提供了和常见的 C 代码结构混合编程的编程方式. 1.基本 ...
随机推荐
- mysql优化之索引优化
Posted by Money Talks on 2012/02/23 | 第一篇 序章第二篇 连接优化第三篇 索引优化第四篇 查询优化第五篇 到实战中去 索引优化 索引优化涉及到几个方面,包括了索引 ...
- How Network Load Balancing Technology Works--reference
http://technet.microsoft.com/en-us/library/cc756878(v=ws.10).aspx In this section Network Load Balan ...
- iOS中利用 runtime 一键改变字体
1.准备 我们新建一个项目名叫ChangeFont,然后我就随便找了个名叫loveway.ttf的字体库拖进去,里面的工程目录大概就是这样的 目录 现在我们就简单的直接在storyboard上拖了一个 ...
- Java IO学习笔记
Java流的分类,一般可按以下方式分: 按方向分,分为输入流,输出流. 按类型分,分为字节流和字符流. 2.1字节流是通过字节来读取数据 2.2字符流是通过字符来读取数据 按操作方式分,分为节点流和过 ...
- Android_Intent_note
Activity间传递参数的方法方法1. 通过putExtra()传递基本数据类型方法2. 通过putExtras()传递Bundle数据类型方法3. 通过Application读写全局变量 方法4. ...
- OpenJDK1.8 安装
1. 安装JDK yum install -y java-1.8.0-openjdk-devel 2. 设置环境变量 /etc/profile文件增加 export JAVA_HOME=/usr/li ...
- Windows Thrift安装及HelloWorld
Thrift是一个facebook开源的高效RPC框架,其主要特点是跨语言及二进制高效传输(除了二进制,也支持json等常用序列化机制),官网地址:http://thrift.apache.org 跨 ...
- MVC中HtmlHelper用法大全参考
MVC中HtmlHelper用法大全参考 解析MVC中HtmlHelper控件7个大类中各个控件的主要使用方法(1) 2012-02-27 16:25 HtmlHelper类在命令System.Web ...
- eclipse和android studio导入工程的错误
eclipse中导入工程,需要注意导入的工程是什么,android 工程和java工程是有区别的.如果导入错误了,调起来也比较麻烦.因为入口错了呀. 特别在android studio工程,从其它人的 ...
- sharedPreference的奇怪bug
一定要清楚sp的结构,而且要知道是什么类型的.类型不对,会引起很多不知道的bug,比如本来是int类型的值,如果用String的类型去匹配,会让Activity开Activity Thread,不断地 ...