OC与Swift混编
一.objectivec与Swift相互调用对照
OC interface文件如下
// 宏定义
#define DefaultHeight 100.f
// 协议
NS_ASSUME_NONNULL_BEGIN
@protocol OCViewControllerDelegate
- (void)detailButtonDidPressed:(id)sender;
@end
@interface OCViewController : UIViewController
// 属性
@property (nonatomic, weak) id delegate;
@property (nonatomic, strong) NSString *testString;
@property (nonatomic, strong) NSArray *testArray;
@property (nonatomic, strong, nullable) NSArray *testArray2;
@property (nonatomic, strong) NSNumber *testNumber;
// 方法
- (void)testMethod1;
- (BOOL)testMethod2WithParam:(NSString *)aParam;
- (NSString *)testMethod3WithParam:(NSArray *)aArray;
- (nullable NSString *)testMethod4WithParam:(nullable NSArray*)aArray;
@end
NS_ASSUME_NONNULL_END
转换后对应的Swift interface文件如下
import UIKit
// 宏定义
public var DefaultHeight: Float { get }
// 协议
public protocol OCViewControllerDelegate : NSObjectProtocol {
public func detailButtonDidPressed(sender: AnyObject)
}
public class OCViewController : UIViewController {
// 属性
weak public var delegate: OCViewControllerDelegate?
public var testString: String
public var testArray: [AnyObject]
public var testArray2: [String]?
public var testNumber: NSNumber
// 方法
public func testMethod1()
public func testMethod2WithParam(aParam: String) -> Bool
public func testMethod3WithParam(aArray: [AnyObject]) -> String
public func testMethod4WithParam(aArray: [AnyObject]?) -> String?
}
二.objc对Swift的兼容
Objective-C类、协议、属性、方法、扩展、闭包等所有功能都可以无缝地被转换为Swift接口被Swift文件所调用。但是还是有些特别需要注意的地方
Objective-C中为了兼容 Swift,新增了三大特性Nullability、Lightweight Generics、__kindof.
1.Nullability
为了适应swift的 Optional ,没有 Swift 中 ? 和 ! 语法的支持,在 Objective-C 中就显得非常啰嗦了,在同一个方法中只要有一个属性被Nullability修饰,所有的方法都必须写,所以不是必要可以都不写,但是细心的话可以看到所有的系统方法基本都已经加入该修饰符,所以这个东西在我看来知识系统适配和极少数情况下的非空指定情况才会出现,毕竟OC是一门弱语言.
- 核心:nullable 与 nonnull 这两个修饰符前者为可为空即? 后边为不可为空即!(我只用过这两个)
不常用: null_resettable 来表示 setter nullable,但是 getter nonnull.
注: 几乎所有能用 C 传统的const关键字的地方都能用__nullable和_nonnull修饰,当然只能用在指针类型上,但是在方法和属性的声明中是不需要写""的即nullable和nonnull,前提是修饰符后边为类对象或block```
- 审核区: 审核区内所有普通的指针类型都会默认为非空
NS_ASSUME_NONNULL_BEGIN //审核区开始
@interface AAPLList : NSObject
// ...
- (nullable AAPLListItem *)itemWithName:(NSString *)name;
- (NSInteger)indexOfItem:(AAPLListItem *)item;// 没有标记
- (void)startWithCompletionBlock:(nullable void (^)(NSError * __nullable error))block;
@property (copy, nullable) NSString *name; //如特殊标记即为标记修饰符属性
@property (copy, readonly) NSArray *allItems;
// ...
@end
NS_ASSUME_NONNULL_END //审核区结束
2.Lightweight Generics (轻量级泛型)
这个一共分为两部分,第一部分很简单,就不多做赘述了,就是泛型类.
- 泛型类: 不知道有啥用
// 只接受 NSNumber * 的泛型
@interface Stack : NSObject
// 只接受满足 NSCopying 协议的泛型
@interface Stack : NSObject
- 容器类泛型: 有了泛型后就可以指定容器类中对象的类型了.
@property (readonly) NSArray *imageURLs;
- 协变性和逆变性:泛型类因为允许接受的类型不同会遇到同类型指针接受不同类型数据的问题.
__covariant - 协变性,子类型可以强转到父类型(里氏替换原则)
__contravariant - 逆变性,父类型可以强转到子类型(WTF?)
3.__kindof
用子类接受父类或者父类接受子类时,不报警告.
三.OC与Swift的异同点
1.构造方法
Swift有重构和重写的概念,但是OC只有重写.
2.循环引用 (weak & unowned)
详见Swift函数
3.基础类型转换 (String、Array、Int)
不同于NSString NSArray NSInteger(不做扩展,使用中慢慢体会)
4.可选类型
Optional类型 即?
详见Swift中的面对对象开发
5.枚举(NS_OPTIONS)
/*
Swift枚举:
Swift中的枚举比OC中的枚举强大, 因为Swift中的枚举是一等类型, 它可以像类和结构体一样增加属性和方法
格式:
enum Method{
case 枚举值
}
*/
enum Method{
// case Add
// case Sub
// case Mul
// case Div
// 可以连在一起写
case Add, Sub, Mul, Div
}
// 可以使用枚举类型变量或常量接收枚举值
var m: Method = .Add
// 注意: 如果变量或常量没有指定类型, 那么前面必须加上该值属于哪个枚举类型
var m1 = Method.Add
// 利用Switch匹配
// 注意: 如果case中包含了所有的值, 可以不写default. 如果case中没有包含枚举中所有的值, 必须写default
switch (Method.Add){
case Method.Add:
print("加法")
case Method.Sub:
print("减法")
case Method.Mul:
print("除法")
case Method.Div:
print("乘法")
// default:
// print("都不是")
}
/*
原始值:
OC中枚举的本质就是整数,所以OC中的枚举是有原始值的,默认是从0开始
而Swift中的枚举默认是没有原始值的, 但是可以在定义时告诉系统让枚举有原始值
enum Method: 枚举值原始值类型{
case 枚举值
}
*/
enum Method2: Int{
// 可以连在一起写
case Add, Sub, Mul, Div
}
// 和OC中的枚举一样, 也可以指定原始值, 后面的值默认+1
enum Method3: Int{
case Add = 5, Sub, Mul, Div
}
// Swift中的枚举除了可以指定整形以外还可以指定其它类型, 但是如果指定其它类型, 必须给所有枚举值赋值, 因为不能自动递增
enum Method4: Double{
// 可以连在一起写
case Add = 5.0, Sub = 6.0, Mul = 6.1, Div = 8.0
}
// rawValue代表将枚举值转换为原始值, 注意老版本中转换为原始值的方法名叫toRaw
print(Method4.Sub.rawValue)
// 原始值转换为枚举值
enum Method5: String{
case Add = "add", Sub = "sub", Mul = "mul", Div = "div"
}
// 通过原始值创建枚举值
/*
注意:
1.原始值区分大小写
2.返回的是一个可选值,因为原始值对应的枚举值不一定存在
3.老版本中为fromRaw("add")
*/
let m2 = Method5(rawValue: "add")
print(m2)
//func chooseMethod(op:Method2)
func chooseMethod(op:String)
{
// 由于返回是可选类型, 所以有可能为nil, 最好使用可选绑定
if let opE = Method5(rawValue: op){
switch (opE){
case .Add:
print("加法")
case .Sub:
print("减法")
case .Mul:
print("除法")
case .Div:
print("乘法")
}
}
}
/*
枚举相关值:
可以让枚举值对应的原始值不是唯一的, 而是一个变量.
每一个枚举可以是在某种模式下的一些特定值
*/
enum lineSegmentDescriptor{
case StartAndEndPattern(start:Double, end:Double)
case StartAndLengthPattern(start: Double, length:Double)
}
var lsd = lineSegmentDescriptor.StartAndLengthPattern(start: 0.0, length: 100.0)
lsd = lineSegmentDescriptor.StartAndEndPattern(start: 0.0, end: 50.0)
// 利用switch提取枚举关联值
/*
switch lsd
{
case .StartAndEndPattern(let s, let e):
print("start = \(s) end = \(e)")
case .StartAndLengthPattern(let s, let l):
print("start = \(s) lenght = \(l)")
}
*/
// 提取关联值优化写法
switch lsd
{
case let .StartAndEndPattern(s, e):
print("start = \(s) end = \(e)")
case .StartAndLengthPattern(let s, let l):
print("start = \(s) lenght = \(l)")
}
6.单例
OC写法:
+ (instancetype)sharedTools {
static dispatch_once_t onceToken;
static NetworkTools *instance;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
Swift写法:
static let sharedTools: SoundTools = SoundTools()
7.值类型和引用类型(计算型属性和存储型属性)
存储型属性:就是可以存储数据,开辟空间并初始化(储存)数据. 计算型属性:个人理解就是没有setter只有getter 所以不开辟控件,在调用时计算其数值.
8.闭包
个人感觉类似于bolck
9.调用Objective-C第三方类库(静态库可直接调用不进行桥接)
10.代理
1.协议只有继承于class才能用weak来修饰代理。 2.协议方法为必须实现。如果不一定实现这个方 法,可以在
protocol之前加@objc,并且在方 法前加
optional,那么这个代理方法就会变成 可选,实现与否都不会影响。 3.用weak修饰代理是防止相互引用造成内存泄漏。 4.代理方法最好在拓展里写,例如: extension ViewController: CustomViewDelegate { .... } 方便代码维护。
11.更多...(后续补充)
四.Swift调用Objective-C
1.桥接文件:
桥接文件“ProjectName-Bridging-Header.h”,在首次创建其他文件的时候,会自动生成。如果不小心删除后,也可以手动添加,不过名字必须是“ProjectName-Bridging-Header.h”头文件(名称组成:工程名-Bridging-Header.h),如果名字记不清也可以自己新建Header file后,在Targets-->Build Settings-->Swift Compiler - General-->Objective-C Bridging Header配置文件路径,这个文件主要是Swift使用OC类时使用。
2.使用文件
当在Swift中使用OC文件的时候,只需在桥接文件即projectName-Bridging-Header.h文件中引入需要的头文件。
具体使用,按照对应的Swift语法结构来即可。
五.Objective-C调用Swift
Xcode会自动为Project生成头文件以便在Objective-C中调用。
在Objective-C类中调用Swift,只需要#import "productModuleName-Swift.h"即可调用,Xcode提供的头文件以Swift代码的模块名加上-Swift.h为命名。
在这个头文件中,将包含Swift提供给Objective-C的所有接口、Appdelegate及自动生成的一些宏定义,如图1-5所示。注意productModuleName-Swift.h在Xcode中是无法搜索查看的,只能从import中点击进去查看。
在大部分情况下,Objective-C都可以无缝地调用Swift,但是由于Swift相对于Objective-C多了一些新特性,比如 泛型、元组、枚举的等,所以Swift暴漏给Objective-C的接口多了一些限制,因此Swift只能暴露在Objective-C中有效的接口。
六.Swift与C交互(极少使用)
有时候我们在Swift中可能需要与C交互(如Core Graphics),Swift对此提供了有效的支持。
1.原始类型
Swift提供了将Swift类型直接映射为C原始类型的“类C风格”类型。这些类型以C开头后跟C原始类型名。例如,bool -> CBool,unsigned long long -> CUnsignedLongLong。
2.指针类型
指针类型需要一些描述信息。例如,const void* -> CConstVoidPointer, void* -> CMutableVoidPointer或者COpaquePointer(两者区别在于用于参数还是函数返回值)。
指向某一类型的指针
3.类型化的指针
可以用泛型语法CMutableVoidPointer
七.其他混编注意事项
1.对于需要混编的Swift类添加@objc声明或继承NSObject或NSObject的子类(在OC中调用Swift代码)
2.使用第三方Framework(很多大场SDK已经兼容不用做此操作)
设置: target-->build setting -->Packaging -->Defines Module为 “Yes”;
然后,配置文件Target -> Build Phases -> Link Binary,添加要导入的Framework;
最后,还是要配置桥接文件,比如要使用 abc-lib.framework库中的 abc.h 就要这样配置:#import"abc-lib/abc.h";3.对于自定义的类而言,Objective-C的类,不能继承自Swift的类,即要混编的OC类不能是Swift类的子类。反过来,需要混编的Swift类可以继承自OC的类。
4.Swift中有许多OC没有的特性,比如,Swift有元组、为一等公民的函数、还有特有的枚举类型。所以,要使用的混编文件要注意Swift独有属性问题.
5.Swift中使用Closure不能使用Block作为属性进行传值,必须是初始化方法或函数。
6.对于需要使用的字符串调用的任何方法,方法名前缀需要使用@objc.
7.字典转模型
系统方法 : Swift4.0新增方法,可对嵌套模型进行转换,异常强大!!!
// JSONDecoder 转模型
let decoder = JSONDecoder()
let data = try! JSONSerialization.data(withJSONObject: statusesDict, options: [])
// 模型数据
let statuses = try! decoder.decode([HMStatus].self, from: data)
print(statusesDict)
// 定义一个临时的空数组
var statusesViewModel = [HMStatusViewModel]()
// 遍历获取回来的 [HMStatus] 数组,生成 [HMStatusViewModel] 数组
for status in statuses {
let viewModel = HMStatusViewModel(status: status)
statusesViewModel.append(viewModel)
}
也可用OC的字典转模型框架方法进行转换,但需在每一个模型属性前天加@objc.
class HMEmoticonModel: NSObject, Codable, NSCoding {
@objc var chs: String?
@objc var png: String?
@objc var type: String?
@objc var code: String?
}