iOS的OC和Swift混编经验总结
1. Swift调用OC的方法进行命名优化
1
2
- (void)testSomeClass:(id<SomeClass>)obj;
- (void)testOtherClass:(id<OtherClass>)obj;
上面的OC方法在Swift中进行调用会合并成同一个
1
Class().test(_ obj:)
在Swift中会主动帮你进行重命名的优化,上面两个OC方法,方法名中的testSomeClass和参数类型SomeClass重复了,Swift就会主动进行优化。
但是优化的形式不是固定的。
1
2
3
@interface RAMXXXTest: XXXTest
- (BOOL)isCaseTest
@end
在swift中进行调用就变成了
1
if test.isCase() {}
2. Swift中使用load和initialize
+load和+initialize是OC中常用的两个方法,在Swift3.0之后的版本中使用会报错
1
2
Method 'load()' defines Objective-C class method 'load', which is not permitted by Swift
Method 'initialize()' defines Objective-C class method 'initialize', which is not permitted by Swift
所以,如果想在 Swift 类中使用这两个方法,则需要求助于 Objective-C,使用变通的方法。
在OC环境中实现Swift类的分类,并实现load和initialize方法调用swift的方法实现。
如下代码所示:
1
2
3
4
5
6
7
8
9
10
11
12
// swift
class Monitor: NSObject {
@objc class func swiftLoad() {
// do something
print("swift load")
}
@objc class func swiftInitialize() {
// do something
print("swift initialize")
}
}
1
2
3
4
5
6
7
8
9
10
11
12
// Objective-C
@implementation Monitor (Private)
+ (void)load {
[self swiftLoad];
}
+ (void)initialize {
[self swiftInitialize];
}
@end
由于这两个方法是 NSObject 类中声明的,所以我们的 Swift 类必须继承自 NSObject 或其子类。另外,我们也可以不用上面这么麻烦地去定义 swiftLoad/swiftInitialize
方法,而是所有操作直接在 Objective-C 代码中完成。
开源库SwiftLoad
也是使用了此方式进行的。
3. Swift方法交换
传统方法
在Swift中实现方法交换必须满足一下条件
- 包含swizzle方法的类需要继承自NSObject
- 需要swizzle的方法必须有动态属性(dynamic attribute)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Father: NSObject {
@objc dynamic func makeMoney() {
print("make money")
}
}
extension Father {
static func swizzle() {
let originSelector = #selector(Father.makeMoney)
let swizzleSelector = #selector(Father.swizzle_makeMoney)
let originMethod = class_getInstanceMethod(Father.self, originSelector)
let swizzleMethod = class_getInstanceMethod(Father.self, swizzleSelector)
method_exchangeImplementations(originMethod!, swizzleMethod!)
}
@objc func swizzle_makeMoney() {
print("have a rest and make money")
}
}
Father.swizzle()
var tmp = Father()
tmp.makeMoney() // have a rest and make money
tmp.swizzle_makeMoney() // make money
使用@_dynamicReplacement(for: )
实现
1
2
3
4
5
6
7
8
9
10
11
12
class Father {
dynamic func makeMoney() {
print("make money")
}
}
extension Father {
@_dynamicReplacement(for: makeMoney())
func swizzle_makeMoney() {
print("have a rest and make money")
}
}
Father().makeMoney() // have a rest and make money
实现原理
method swizzling都是利用runtime类获取selector的实现,来动态改变。Swift通过OC的Runtime特性来实现。
在Swift中使用dynamic属性来标记方法,是该方法只在运行期进行编译,以达到动态派发。
4. Swift中使用关联对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
extension UIViewController {
private struct AssociatedKeys {
static var DescriptiveName = "nsh_DescriptiveName"
}
var descriptiveName: String? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.DescriptiveName) as? String
}
set {
if let newValue = newValue {
objc_setAssociatedObject(
self,
&AssociatedKeys.DescriptiveName,
newValue as NSString?,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
)
}
}
}
}
5. OC中的@class
Swift混编中,引用到的一个OC类中,用的是向前申明的类,并且这个类没有在桥接文件中,就会出现报错
Type '' cannot conform to protocol '' because it has requirements that cannot be satisfied
1
2
3
4
@class TestClass
@protocol ObjProtocol <NSObject>
- (void)testFunc:(TestClass *)arg;
@end
尝试在swift中使用ObjProtocol
就会报错
1
@objc class SwiftClass: NSObject, ObjProtocol { }
6. Swift中消息通知的使用方式
在Swift中使用通知,通知命名没有想OC那样轻松,只需要一个字符串就能搞定。
1
Notification.Name("myNoti")
可使用如下两种方式进行扩展
1. 使用extension扩展Notification.Name
1
2
3
4
5
extension Notification.Name {
static let myNoti = Notification.Name("myNoti")
}
NotificationCenter.default.post(name: .myNoti, object: nil)
2. 使用Enum定义自己的通知名称
1
2
3
4
5
6
7
8
9
enum NotificationNames: String {
case myNoti
var nameValue: String {
return "RAM" + rawVlue
}
var notiName: Notification.Name {
return Notification.Name(nameValue)
}
}
再定义一个自定义的post函数,方便调用。
1
2
3
func post(_ name: NotificationNames, object myObj: AnyObject? = nil) {
NotificationCenter.default.post(name: name.myNoti, object: myObj)
}
使用起来就简单了
1
NotificationCenter.default.post(name: .myNoti, object: nil)
7. 可变类型容器转换
NSMutableDictionary
在swift中不会默认的将它转化为[[AnyHashable: Any]]
,所以在Swift中需要使用OC的容器类型。建议在混编环境下,使用不可变容器。
1
2
3
4
5
6
7
8
9
10
11
// 赋值
let headers: NSMutableDictionary = NSMutableDictionary()
if let tmp = responseHeader as? [String: Any] {
_ = tmp.map { multi.setValue($0.value, forKey: $0.key) }
response.headers = headers
}
// 取值
if let responseHeaders = response.headers as? [AnyHashable: Any] {
model.responseHeaders = responseHeaders
}
8. Weak Strong
1
2
3
4
5
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
// 这里就是代表了strong的含义
guard let self = self else { return }
self.elapsedTime += 1
}
9. where
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 放在switch中的case里面当做条件判断
var value:(Int,String) = (1,"小明")
switch value {
case let (x,_) where x < 60:
print("不及格")
default:
print("及格")
}
// 放在forin循环里面当做条件判断
let arrayOne = [1,2,3,4,5]
let dictionary = [1:"hehe1",2:"hehe2"]
for i in arrayOne where dictionary[i] != nil {
print(i)
}
// 泛型里面,标识只支持实现了ExpressibleByStringLiteral的泛型
func genericFunction<S>(str:S) where S:ExpressibleByStringLiteral{
print(str)
}
// 也可以不使用where语句,直接在尖括号中定义时做限制
func genericFunction2<S:ExpressibleByStringLiteral>(str:S){
print(str)
}
// 放在扩展力里面
protocol aProtocol{}
extension aProtocol where Self:UIView{
//只给遵守myProtocol协议的UIView添加了扩展
func getString() -> String{
return "string"
}
}
10. 泛型
我们学习下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 实现Base泛型类型的JKPop
public struct JKPOP<Base> {
let base: Base
init(_ base: Base) {
self.base = base
}
}
public protocol JKPOPCompatible {}
public extension JKPOPCompatible {
static var jk: JKPOP<Self>.Type {
get{ JKPOP<Self>.self }
set {}
}
var jk: JKPOP<Self> {
get { JKPOP(self) }
set {}
}
}
/// Define Property protocol
internal protocol JKSwiftPropertyCompatible {
/// Extended type
associatedtype T
///Alias for callback function
typealias SwiftCallBack = ((T?) -> ())
///Define the calculated properties of the closure type
var swiftCallBack: SwiftCallBack? { get set }
}
OC方法在Swift中的使用表
常用函数相关
OC | Swift |
---|---|
NSMutableArray的方法removeObjectsInArray:objects = @[@"str1",@"str1"] [tt removeObjectsInArray:objects]; | 可以使用高阶函数filtertt.filter { value in !objects.contains(value) } |
respond和perform | let selector = NSSelectorFromString("testFunc") if self.obj?.responds(to: selector) == true self.obj?.perform(selector) |
语法相关
OC | Swift |
---|---|
block当做函数入参 | 需要是用@escaping,代表逃逸闭包 |
switch显示贯穿,需要每个case结尾break | 隐式贯穿,不需要加break |
@weakify @strongify | [weak self] |
判空 | 使用 guard let aa = aa else { return }的方式 |
类型转化 | 使用 guard let aa = aa as? SomeClass else { return }的方式 |
懒加载 | lazy var obj: Object = { return Object() }() 只执行一次的懒加载 |
delloc | deinit |
枚举初始LogLevel level = 1 | LogLevel(rawValue: 1) |
C语言宏 | 没有宏,要使用let或是函数 |
参考文章
Swift & the Objective-C Runtime
Apple: using objective-c runtime features in Swift
Swift 5 之后 “Method Swizzling”?: 介绍了 @_dynamicReplacement(for: )
和连环Hook
的场景