OC与Swift混编

OC与Swift混编

一.objectivec与Swift相互调用对照

OC interface文件如下

// 宏定义
#define DefaultHeight 100.f

// 协议
NS_ASSUME_NONNULL_BEGIN
@protocol OCViewControllerDelegate <NSObject>
- (void)detailButtonDidPressed:(id)sender;
@end

@interface OCViewController : UIViewController

// 属性
@property (nonatomic, weak) id<OCViewControllerDelegate> delegate;
@property (nonatomic, strong) NSString *testString;
@property (nonatomic, strong) NSArray  *testArray;
@property (nonatomic, strong, nullable) NSArray<NSString *> *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 <NSCoding, NSCopying>  
// ...  
- (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<objecttype: nsnumber *> : NSObject

// 只接受满足 NSCopying 协议的泛型
@interface Stack<objecttype: id> : NSObject
  • 容器类泛型: 有了泛型后就可以指定容器类中对象的类型了.
@property (readonly) NSArray <NSURL *>*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第三方类库(静态库可直接调用不进行桥接)

image.png

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中点击进去查看。

图1-5,Xcode自动生成SwiftProject-Swift.h文件"

在大部分情况下,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<Type>,例如,Int* -> CMutableVoidPointer<Type>

七.其他混编注意事项

  • 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?
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • swift 语言出来后,可能新的项目直接使用swift来开发,但可能在过程中会遇到一些情况,某些已用OC写好的类或...
    wzf_taker阅读 2,171评论 0 2
  • 一、创建桥接文件 下面我们先建个Objc的工程,在Objc工程中新建Swift文件时,(或者在Swift工程新建O...
    海岸没有沙阅读 735评论 0 0
  • OC调用Swift 1.新建OC项目工程 2.在项目中新建swft Controller文件 点击 "Create...
    AceKitty阅读 590评论 0 0
  • swift强势来袭,想要紧跟脚步,但之前OC的工程推倒重来在时间和实践上都有点伤神,混编就是这个过渡的桥梁,使...
    _skye阅读 5,173评论 8 22
  • OC中使用Swift 1、在项目中右键添加一个 Swift 文件,如TestSwift.swift。 2、添加后 ...
    刺客辣条阅读 2,187评论 3 11