@OBJC 和 DYNAMIC
原文转载自:@OBJC 和 DYNAMIC
虽然说 Swift 语言的初衷是希望能摆脱 Objective-C 的沉重的历史包袱和约束,但是不可否认的是经过了二十多年的洗礼,Cocoa 框架早就烙上了不可磨灭的 Objective-C 的印记。无数的第三方库是用 Objective-C 写成的,这些积累无论是谁都不能小觑。因此,在最初的版本中,Swift 不得不考虑与 Objective-C 的兼容。
Apple 采取的做法是允许我们在同一个项目中同时使用 Swift 和 Objective-C 来进行开发。其实一个项目中的 Objective-C 文件和 Swift 文件是处于两个不同世界中的,为了让它们能相互联通,我们需要添加一些桥梁。
首先通过添加 {product-module-name}-Bridging-Header.h 文件,并在其中填写想要使用的头文件名称,我们就可以很容易地在 Swift 中使用 Objective-C 代码了。Xcode 为了简化这个设定,甚至在 Swift 项目中第一次导入 Objective-C 文件时会主动弹框进行询问是否要自动创建这个文件,可以说是非常方便。
但是如果想要在 Objective-C 中使用 Swift 的类型的时候,事情就复杂一些。如果是来自外部的框架,那么这个框架与 Objective-C 项目肯定不是处在同一个 target 中的,我们需要对外部的 Swift module 进行导入。这个其实和使用 Objective-C 的原来的 Framework 是一样的,对于一个项目来说,外界框架是由 Swift 写的还是 Objective-C 写的,两者并没有太大区别。我们通过使用 2013 年新引入的 @import 来引入 module:
@import MySwiftKit;
之后就可以正常使用这个 Swift 写的框架了。
如果想要在 Objective-C 里使用的是同一个项目中的 Swift 的源文件的话,可以直接导入自动生成的头文件 {product-module-name}-Swift.h 来完成。比如项目的 target 叫做 MyApp 的话,我们就需要在 Objective-C 文件中写
#import "MyApp-Swift.h"
但这只是故事的开始。Objective-C 和 Swift 在底层使用的是两套完全不同的机制,Cocoa 中的 Objective-C 对象是基于运行时的,它从骨子里遵循了 KVC (Key-Value Coding,通过类似字典的方式存储对象信息) 以及动态派发 (Dynamic Dispatch,在运行调用时再决定实际调用的具体实现)。而 Swift 为了追求性能,如果没有特殊需要的话,是不会在运行时再来决定这些的。也就是说,Swift 类型的成员或者方法在编译时就已经决定,而运行时便不再需要经过一次查找,而可以直接使用。
显而易见,这带来的问题是如果我们要使用 Objective-C 的代码或者特性来调用纯 Swift 的类型时候,我们会因为找不到所需要的这些运行时信息,而导致失败。解决起来也很简单,在 Swift 类型文件中,我们可以将需要暴露给 Objective-C 使用的任何地方 (包括类,属性和方法等) 的声明前面加上 @objc 修饰符。注意这个步骤只需要对那些不是继承自 NSObject 的类型进行,如果你用 Swift 写的 class 是继承自 NSObject 的话,Swift 会默认自动为所有的非 private 的类和成员加上 @objc。这就是说,对一个 NSObject 的子类,你只需要导入相应的头文件就可以在 Objective-C 里使用这个类了。
@objc 修饰符的另一个作用是为 Objective-C 侧重新声明方法或者变量的名字。虽然绝大部分时候自动转换的方法名已经足够好用 (比如会将 Swift 中类似 init(name: String) 的方法转换成 -initWithName:(NSString *)name 这样),但是有时候我们还是期望 Objective-C 里使用和 Swift 中不一样的方法名或者类的名字,比如 Swift 里这样的一个类:
class 我的类: NSObject {
func 打招呼(名字: String) {
print("哈喽,\(名字)")
}
}
我的类().打招呼("小明")
注:在 Swift 2.0 中,Apple 在从 Swift 导出头文件时引入了一个叫做
SWIFT_CLASS_NAMED的宏来对原来 Swift 中的内容进行标记。这个宏使用 LLVM 的标记来对目标类的类型做出了限制,但是同时引入了不允许非 ascii 编码的问题。下面的代码在 Swift 1.x 环境下可以通过,但是在 Swift 2 中会导致 “Parameter of 'swift_name' attribute must be an ASCII identifier string” 的编译错误,这应该是 Swift 2.0 中的一个预期之外的倒退。笔者已经向 Apple 提交了 bug 报告。关于这个问题的更多信息,可以参考 rdar://22737851 和这里的讨论。
Objective-C 的话是无法使用中文来进行调用的,因此我们必须使用 @objc 将其转为 ASCII 才能在 Objective-C 里访问:
@objc(MyClass)
class 我的类 {
@objc(greeting:)
func 打招呼(名字: String) {
print("哈喽,\(名字)")
}
}
这样,我们在 Objective-C 里就能调用 [[MyClass new] greeting:@"XiaoMing"] 这样的代码了 (虽然比起原来一点都不好玩了)。另外,正如上面所说的以及在 Selector 一节中所提到的,即使是 NSObject 的子类,Swift 也不会在被标记为 private 的方法或成员上自动加 @objc,以保证尽量不使用动态派发来提高代码执行效率。如果我们确定使用这些内容的动态特性的话,我们需要手动给它们加上 @objc 修饰。
但是需要注意的是,添加 @objc 修饰符并不意味着这个方法或者属性会变成动态派发,Swift 依然可能会将其优化为静态调用。如果你需要和 Objective-C 里动态调用时相同的运行时特性的话,你需要使用的修饰符是 dynamic。一般情况下在做 app 开发时应该用不上,但是在施展一些像动态替换方法或者运行时再决定实现这样的 "黑魔法" 的时候,我们就需要用到 dynamic 修饰符了。在 KVO 一节中,我们提到了一个关于使用 dynamic 的实例。
关于 Swift 和 Objective-C 混用的一个好消息是,随着 Swift 的发展,Apple 正在努力改善 SDK。在 Objective-C 中添加的 nonnull 和 nullable,以及泛型的数组和字典等,其实上都是为了使 SDK 更加适合用 Swift 来使用所做的努力,我们还是很有希望在不久的未来能够摆脱掉这些妥协和束缚的。
@OBJC 和 DYNAMIC的更多相关文章
- @objc and dynamic
@objc and dynamic Objective-C runtime visibility and the depths of dynamic dispatch in the modern ...
- Swift与Objective-C的兼容“黑魔法”:@objc和Dynamic
Cocoa框架早已烙上了不可磨灭的OC印记,而无数的第三方库都是用OC写成的,这些积累无论是谁都不能小觑.苹果采取了允许开发者在同一个项目中同时使用Swift和OC进行开发的做法,但要想实现互通,又需 ...
- swift @objc dynamic
@objc vs @objc dynamic @objc: Objective-C entry points One can explicitly write @objc on any Swift ...
- Swift和OC混编时, 关于@objc的作用
Objective-C 和 Swift 在底层使用的是两套完全不同的机制,Cocoa 中的 Objective-C 对象是基于运行时的,它从骨子里遵循了 KVC (Key-Value Coding,通 ...
- swift的@objc总结
One can explicitly write @objc on any Swift declaration that can be expressed in Objective-C. @objc相 ...
- ObjC之RunTime(上)
转载自这里. 最近看了一本书——iOS6 programming Pushing the Limits(亚马逊有中文版),最后一章是关于Deep ObjC的,主要内容是ObjC的runtime.虽然之 ...
- iOS ---Swift学习与复习
swift中文网 http://www.swiftv.cn http://swifter.tips/ http://objccn.io/ http://www.swiftmi.com/code4swi ...
- swifter技巧(100)
一.swift新元素 Tip1:柯里化 将方法进行柯里化,把接受多个参数的方法变换成接受第一个参数的方法,并且返回接受余下的参数,返回结果的新方法. func addTwoNumbers(a: Int ...
- [swift] NSClassFromString 无法获得该类
在写OC的时候需要用 NSClassFromString(classStringName)获得一个类,如果存在就用这个类型来声明一个对象, 但是在swift的时候却往往得不到这个类,为什么呢? 从截图 ...
随机推荐
- SwipeBackActivity 的使用
1.SwipeBackLayout 项目地址:https://github.com/ikew0ng/SwipeBackLayout 2.用法 android studio compile 'me.i ...
- Android的进程等级
Android五个进程等级 1.前台进程(Foreground process): 用户当前工作所需要的.一个进程如果满足下列任何条件被认为是前台进程: 正运行着一个正在与用户交互的活动(Activi ...
- 【代码笔记】iOS-时间选择框
一, 效果图. 二,工程图. 三,代码. RootViewController.h #import <UIKit/UIKit.h> @interface RootViewControlle ...
- sublime mac osx 命令行打开
sudo ln -s /Applications/Sublime\ Text\ 2.app/Contents/SharedSupport/bin/subl /usr/bin/subl 参考地址:htt ...
- SQL Server日期时间格式转换字符串
在SQL Server数据库中,SQL Server日期时间格式转换字符串可以改变SQL Server日期和时间的格式,是每个SQL数据库用户都应该掌握的.本文我们主要就介绍一下SQL Server日 ...
- 启用Mac(OS X Yosemite)自带的apache
刚用Mac的时候配置过一次Mac自带的apache,主要是平常自己用mackdown写文档,装成html文件放到apache下方便自己和同事阅读.后来升级各种东西,估计是升级OS X导致apache不 ...
- mysql锁机制总结
1.隔离级别 (1)读不提交(Read Uncommited,RU) 这种隔离级别下,事务间完全不隔离,会产生脏读,可以读取未提交的记录,实际情况下不会使用. (2)读提交(Read commited ...
- SQL中CHARINDEX()/INSTR()函数和SUBSTRING()/SUBSTR()函数
一.SQLServer中的CHARINDEX() 和ORACLE中的INSTR()函数 1.INSTR(C1,C2[,I[,J]]) [功能]在一个字符串中搜索指定的字符,返回发现指定的字符的位置; ...
- HDFS分布式文件系统资源管理器开发总结
HDFS,全称Hadoop分布式文件系统,作为Hadoop生态技术圈底层的关键技术之一,被设计成适合运行在通用硬件上的分布式文件系统.它和现有的分布式文件系统有很多共同点,但同时,它和其他的分布式 ...
- 关于oracle数据库报12505错误的问题!
问题阐述: 导致oracle报12505错误的原因比较多,但是最可能一种原因就是客户端的监听出了问题. 解决办法: 在oracle安装目录下找到listener.ora 和 tnsnames.ora, ...