OC与Swift混编(基于Swift 4.1)
1、在OC项目中,新建一个swift文件后会提示要不要自动新建Bridging_Header
,由于项目中存在多个target,自动新建会新建多个,所以不是很推荐这种方式,自己新建也很方便,新建header file
后,在Build Settings -> Swift Compiler - general -> Objective-C Bridging Header
中写入即可,然后在Objective-C Bridging Header
的下方有一栏Objective-C Generated Interface Header Name
是系统生成的文件,固定格式为项目名-Swift.h
。
-
Bridging_Header
文件可以理解为swift版本的PCH文件,但只能给swift文件使用,由于swift文件无法直接import
OC文件,混编时,需要在Bridging_Header
中import好OC类; -
项目名-Swift.h
这个文件是OC调用swift类需要用到的,同理上一条,OC中也是无法直接import
swift的,但是可以import
这个文件,并且项目名-Swift.h
也是不需要我们自己维护,只要OC中import
了该文件,就可以直接使用了,所以我是把项目名-Swift.h
放在了OC的PCH中。
2、(重要
)网上混编的文章都是通过demo来演示的,并没有实际项目中复杂且各种父类宏的使用,所以在说完第一点之后就直接开始编写了,并没有提到可能会遇到的坑,我虽然只修改了一个VC,但总结下来的话也有下面几点:
- 在
Bridging_Header
中import
是没有提示的!!! - 在
Bridging_Header
中import
是不会加载被import
文件中的其他类,如果用到需要另外再import
; - 在
Bridging_Header
中被import
的OC文件一定要确保用到的类被正确import
,否则会报文件未找到; - 由于Swift无法公用OC的宏定义,所以还要确保在
Bridging_Header
中被import
的.h文件没有用到OC的宏定义,例如通用的block回调之类。
在写代码过程中,可能会遇到系统提示fix给参数添加了? ! ??
之类的字符。
可选值
打包wrap
(?
)
?
表示有值或者为空。Eg: var name : String?
表示name可能有值也可能为空,可以理解为可选值类型是一个盒子,这个盒子有有值和无值两种情况,也就是可选值变量对有值或无值进行了打包操作。
var a :Int?
var b :Int
虽然都是Int类型,但实际上a和b是不一样的,a属于可选Int类型,b属于Int类型
。
- eg:
var aaa: Int? = 30
print(aaa)
会报一个Expression implicitly coerced from 'Int?' to Any
的警告,意思是把可选值类型隐式地强制转换成任意类型来处理,可修改为print(aaa as Any)
消除警告。
但这并不好,因为还有隐患
print(aaa + 1)
这样更能直白的表示出可选值和非可选值的区别,会直接报错:Value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?
,提示没有对Int?进行解包unwrapped
,且直接对可选类型做了加一操作,Swift是强类型语言,在编译阶段就为我们排除了很大一部分的类型不匹配的错误,例如OC中int和float类型相加并不会报错隐式类型转换
,而在Swift中则不行,所以这里相当于是将可选值的盒子进行了加一的操作,而不是对可选值盒子中的内容进行加一,就报错了。
解包Unwarpping optionals
既然有打包,那么肯定就有解包,就是把值从盒子内取出来
- 1、强制解包
Force unwrapping
(!
)
针对上面的例子
print(aaa! + 1)
这样就解包了,变量名后的感叹号告诉编译器,我想看下盒子里面的内容并取出来里面的值
,但是,使用强制解包应该谨慎的使用。
还是针对上面的例子,由于aaa
被定义为可选值变量,在使用过程中可能会置换为nil
var aaa: Int? = 30
aaa = nil
print(aaa! + 1)
这样就会报错Fatal error: Unexpectedly found nil while unwrapping an Optional value
,错误原因是对一个值为空的变量进行了解包,当然可以在每次解包的时候判断是否为nil
,但这无疑是增加了自己的代码及工作量,并且如果万一忘记了的话,程序还是会崩溃。
所以对于强制解包,推荐是只有在确定不为空的情况下才使用的
- 可选值绑定
Optional binding
(iflet
)
var aaa: Int? = 30
aaa = nil
if let bbb = aaa {
print(bbb + 1)
} else {
print("no")
}
上面的代码使用了iflet
表达式来进行了绑定,如果可选值内容不为空,那么就会被解包,并且let
的常量名可定义为相同的,如:
var aaa: Int? = 30
aaa = nil
if let aaa = aaa {
print(aaa + 1)
} else {
print("no")
}
- 空值合并
Nil coalescing
(??
)
var aaa: Int? = 30
aaa = nil
var bbb = aaa ?? 20
print(bbb + 1)
这也是一种解包,叫做空合运算符,类似于三目运算符,上面??
的意思表示如果aaa不为nil
,那么就将aaa的值给bbb,否则将bbb赋值为20。