你可以在 xcode 里同时使用 Swift 和 Objective-C(以下简称OC)来写代码,混搭编程的好处很多,比如允许大量代码的复用,在性能和开发效率之间找到平衡等。
我们建立一个工程时,XCode会询问我们选择什么语言进行开发,如果你选择的是OC,那么当你第一次新建一个swift文件时,开发环境会询问你是否建立一个 .h 文件。这个 .h 文件命名方式是 "#ProjectName#-Bridging-Header.h" 。你可以让开发环境帮你创建这个文件,也可以自己建,编译器只认这个文件名,只要别写错就好。
我们称这个文件叫桥接文件,它的作用是把OC代码选择性的暴露给swift,让swift可以调用这些接口。
暴露的方式是通过import,也就是说,你可以在这个 .h 文件里 import 任何你想要暴露给swift的代码,然后就可以在 swift 中访问了。
动手实践:
打开xcode,创建一个OC工程 HelloWorld
创建一个叫 HelloOC 的 OC 类,实现静态方法 hello,实现代码 NSLog("hello, OC.")
创建一个叫 HelloSwift 的 swift 类,继承NSObject,实现静态函数 hello(),实现代码 NSLog("hello, swift.")
这时候 xcode 会弹出询问是否创建文件 HelloWorld-Bridging-Header.h ,选择是,如果你点了否,就自己创建一个
在 HelloWorld-Bridging-Header.h 代码里,加入 #import "HelloOC.h"
在HelloSwift 的 hello 函数里,在 NSLog("hello, swift.") 后追加 HelloOC.hello()
注意,继承自 NSObject 这点很重要,因为OC所有类都是继承自 NSObject,而 swift 没有这个要求,所以如果需要暴露 swift 的类给 OC ,一定必须是 NSObject 的子类才行。
如果编译成功了,那就说明编译器允许让你通过 HelloSwift 调用 HelloOC 的代码了。
这时候执行程序,会发现输出终端并没有打印任何东西。因为程序主体本身并没有调用 HelloSwift,我们建立的是 OC 工程,所以这时候就需要 OC 来调用 swift代码了。
当我们建立 HelloSwift 时,xcode 其实做了一些后台工作,除了询问你是否建立 "HelloWorld-Bridging-Header.h" 外,它还隐式的创建了一个叫 "HelloWorld-Swift.h" 的头文件,记住,这个文件是 xcode 隐式创建的,所以不要自己去建立这个文件,很多人查资料发现需要这个头文件没看仔细就自己去创建,结果导致各种编译不通过。
这个 HelloWorld-Swift 文件从文件到代码都是 xocode 动态生成的,你不需要编辑它,如果感兴趣里面到底写了什么,你可以通过 import 这个文件,Jump To Definition 的方式一探究竟。
当你需要暴露 swift 的类给 OC 调用时,你不需要通过任何逐个 import 的方式,你只要 import "HelloWorld-Swift.h" 即可。
尝试步骤如下:
在 ViewController.m 文件中,引入头文件 #import "HelloWorld-Swift.h"
在 ViewDidLoad 方法的实现中,调用 [HelloSwift hello];
这时候编译,执行,工程打印日志输出为:
Hello, Swift.
Hello, OC.
开发项目经常要引用第三方框架,在 swift 中,引用这些框架是非常简单的,只要在 HelloWorld-Bridging-Header.h 中用 @import 语句包含该框架即可。不管该框架是用什么 swift 还是 OC 写的,又或者是混合编写,用法都一样。
而如果是用 OC 引用这些框架的话,标准做法应该是:
在 .m 文件中,用 @import 语句引用该框架
在 .h 文件中,如果需要在接口中声明对应的类,则应该用 @class 做前置声明,用这样的做法来规避循环引用问题。
举例 HelloWorld 项目来说,当我们要在 HelloSwift.swift 中引用一个 SwiftFrameWork 框架时,正确的做法是:
在 HelloWorld-Bridging-Header.h 中加入 @import SwiftFrameWork;
在 HelloSwift.swift 中自由调用 SwiftFrameWork 的类。
而如果要在 HelloOC.h 和 HelloOC.m 中这么做,则标准做法应该是:
在 HelloOC.m 中,通过 @import SwiftFrameWork;
在 HelloOC.h 中,如果有需要引用到的 SwiftFrameWork 框架中的类,用前置声明的方法解决,比如如果需要引用类 ClassA,则可以在引用前声明 @class ClassA;
如果需要在 HelloOC.h 中引用到 HelloSwift 类,也应该遵循步骤 2 的做法。
对于swift 的 protocol,也可以暴露给 OC 调用,但是需要做一些额外的工作,需要针对要暴露的 swift protocol 添加 @objc 声明,并且对于 optional 函数也要追加 @objc @optional
在 HelloSwift.swift 追加协议代码:
// 因为这个 protocol 要暴露给 OC 用,所以用 @objc 声明 @objc protocol HelloProtocol { // 这是一个普通的swift协议函数 func protocolFunction() // 这是个 optional 函数,需要在前面追加 @objc 声明 @objc optional func optionalProtocolFunction() }
一旦完成了以上操作,OC类即可声明和实现对应的协议函数为其他类提供回调实现。
swift 和 OC 之间的错误码共享方案很简单,就是简单的命名映射技术,在 swift 中定义错误码如下:
@objc public enum CustomError: Int, Error { case a, b, c }
则 xcode 会在 HelloWorld-Swift.h 中声明对应的面向 OC 的错误码:
typedef SWIFT_ENUM(NSInteger, CustomError) { CustomErrorA = 0, CustomErrorB = 1, CustomErrorC = 2, }; static NSString * _Nonnull const CustomErrorDomain = @"HelloWorld.CustomError";
命名映射的规则比较简单明了,Enum 类型名不变,实例的名称通过大骆驼命名法进行拼接。
HelloOC.h
HelloOC.m
HelloSwift.swift
ViewController.m
苹果为保证 swift 和 OC 顺利交互,做了大量严谨的工作,这里就不一一说明了,毕竟道理大同小异,在需要的时候查阅文档就可以了,类似需要查阅的知识点有:
用 NS_REFINED_FOR_SWIFT 宏重定义 OC 接口
手动为 swift 类指定一个映射名给 OC 调用(通过 @objc)
通过 NS_SWIFT_NAME 指定 swift 自定义名称
...
Using Swift with Cocoa and Objective-C (Swift 4)
author: Madaxin
email: madaxin@outlook.com
qq group: 527628370
home: madaxin.com
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。