swift3.0和Objective-C的交互需要注意这些

时间:2023-01-31 19:05:18

投稿文章,作者:ZeroJ(Gitbub

前言:

Swift3.0出来后, 可以看到改变很大, 和cocoa, Foundation...的交互也变得更方便了, 同时Swift编写的应用适配到iOS 7, 所以, 我们可以相信: 在未来使用swift的情况会逐渐增加了, 同时会涉及到OC和Swift在项目中并存的情况, 这里我重新读了官方的'Using swift with Cocoa and Objective-C(swift3)'的文档, 记录了一些个人觉得比较常用的笔记, 请大家选择性阅读(里面的代码 均来自文档)

  • OC的初始化方法在Swift中被引为

123  init(...) --- 如果初始化不会失败 init?(...) --- 如果初始化可能失败 init!(...) --- 否则
  • oc中的property里的(getter==, setter==)将会被swift忽略

  • id对应Anyobject 但是所有的Anyobject在swift中是可选值, 如果之前的值为可选值, 在被设置为Anyobject后就是多重可选值了

  • oc中的属性被标记为

123 nullable -> 在swift中相当于 ?nonnull -> 在swift中相当于 非可选属性未标记 -> 在swift中相当于 !
  • oc中的轻量级泛型也是对应与swift中的泛型

12 @property NSArray *dates对应于  var dates: [Date]
  • swift 中的闭包默认捕获变量的方式相当于 oc中block中捕获被标记为 __block的变量方式 -> 就是说 闭包捕获到的是变量的指针

  • swift中只要不是在多线程中, 建议使用[unowned self]来避免循环引用, 多线程中, 建议使用[weak self]

  • == 操作符相当于oc中的isEqual: --- 即比较内容是否相等;=== 相当于oc中的指针比较

  • 继承自NSObject的子类如果重写了isEquals:方法, 应当提供 hash 这个属性

  • 不能在oc中继承swift的class

  • 如果在swift中遇到oc中不支持的命名 可以利用 @objc(name)为他(属性 枚举, 方法名...)名个别名

  • @nonobjc 用来标记oc中不支持的

  • dynamic 将属性或者方法标记为dynamic就是告诉编译器把它当作oc里的属性或方法来使用(runtime),

  • 当需要使用 KVO 或者 runtime的时候需要这样处理

  • 当使用oc的 perform(selector, with:)方法的时候会返回一个可选值(指向AnyObject的指针);但是使用perform(:on:with:waitUntilDone:modes:) and perform(:with:afterDelay:)不会返回可选值

  • 使用 #keyPath() 可以转换为string, #keyPath(class.property) == "property"

  • 可用于KVC 例如person.value(forKey: #keyPath(Person.name)) = person.name

  • 但是测试了下不能修改swift中的只读属性 不知道有什么方便的用处

  • NSClassFromString("MyFramework.MyClass")

  • @IBDesignable 用在class(UIView的子类)声明的前面, 然后就可以在storyBoard中的inspector编辑它;@IBInspectable 用在(UIView的子类)的属性前面, 然后就可以在storyBoard中的inspector编辑它 ,就想系统提供的可以设置颜色,字体...

  • swift中的属性默认是strong类型, 只有可选类型才能被标记为weak

  • oc中的 copy属性 转换为swift中的@NSCopying 必须遵守NSCoding协议

  • 使用Core Data的时候所有的属性和方法需要标记为 @NSManaged

  • 文档中指出"The corresponding reference types can be accessed with their original NS class name prefix."但是beta版本中不能很好的使用NS开头的

  • 在oc和swift的桥接类型之间 直接使用 as 可以相互转换

  • 因为swift中的String和NSString使用的编码方式不一样,所以在swift中要对string使用索引的时候 不能直接使用 Int 或者NSRange

  • 需要使用String.Index and Range

  • swift会将Double, Int, Bool, Uint, Float和NSNumber桥接, 所以可以直接将

  • 这些类型的值使用 as NSNumber转换为NSNumber, 但是逆向进行是得到的可选值 as?

  • Foundation 和Core Foundation 之间的类型有toll-free bridge('免费桥')

  • Foundation中的常量, 在swift中被换为类嵌套的枚举:NSJSONReadingOptions ----- >> JSONSerialization.ReadingOption

  • swift中使用 Core Foundation

  • 如果使用swift处理过的函数不用我们手动管理内存分配;否则需要我们处理

  • 区分的方式: 当返回值是 Unmanaged的时候说明需要我们处理

  • 处理方法: 在使用返回的值之前调用他对应的takeUnretainedValue() 或takeRetainedValue()即可

  • 例如let memoryManagedResult = StringByAddingTwoStrings(str1, str2).takeUnretainedValue()

  • swift中Core Foundation里的类型 Ref后缀被去掉了 例如 CFTypeRef -> CFType

  • 在oc的方法 使用 NS_SWIFT_NOTHROW , 将不会使用swift的异常抛出机制

  • swift中直接使用 is 来实现oc中isKindOfClass: 的功能

  • swift中使用kvo的条件: 1.必须继承自NSObject 2. 被观察的属性 要被标记为 dynamic

  • swift 中的单例很简单:

123 class Singleton {  static let sharedInstance = Singleton()}

或者

1234567 class Singleton {  static let sharedInstance: Singleton = {      let instance = Singleton()      // setup code      return instance  }()}
  • swift和C的交互: c的函数在swift中均为全局函数

  • 使用CF_SWIFT_NAME 这个宏可以将c中的属性或者函数转换为swift中

eg:

1 Color ColorCreateWithCMYK(float c, float m, float y, float k) CF_SWIFT_NAME(Color.init(c:m:y:k:));

对应为swift中

123 extension Color {  init(c: Float, m: Float, y: Float, k: Float)}
  • c语言中的枚举 如果使用了NS_ENUM定义, 则在swift中被处理为对应的枚举

  • 如果没有使用NS_ENUM定义, 则被处理为结构体

123456789101112131415161718 typedef NS_ENUM(NSInteger, UITableViewCellStyle) { UITableViewCellStyleDefault, UITableViewCellStyleValue1, UITableViewCellStyleValue2, UITableViewCellStyleSubtitle};//对应与enum UITableViewCellStyle: Int {  case `default`  case value1  case value2  case subtitle}typedef enum { MessageDispositionUnread = 0, MessageDispositionRead = 1, MessageDispositionDeleted = -1,} MessageDisposition;

对应与

1234 struct MessageDisposition: RawRepresentable, Equatable {}var MessageDispositionUnread: MessageDisposition { get }var MessageDispositionRead: MessageDisposition { get }var MessageDispositionDeleted: MessageDisposition { get }

c中的被NS_OPTIONS修饰的枚举, 在swift中是OptionSet类型, -> 即可以使用数组方式选多个值

123456789 typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {      UIViewAutoresizingNone                 = 0,      UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,      UIViewAutoresizingFlexibleWidth        = 1 << 1,      UIViewAutoresizingFlexibleRightMargin  = 1 << 2,      UIViewAutoresizingFlexibleTopMargin    = 1 << 3,      UIViewAutoresizingFlexibleHeight       = 1 << 4,      UIViewAutoresizingFlexibleBottomMargin = 1 << 5};

对应与

123456789 public struct UIViewAutoresizing : OptionSet {  public init(rawValue: UInt)  public static var flexibleLeftMargin: UIViewAutoresizing { get }  public static var flexibleWidth: UIViewAutoresizing { get }  public static var flexibleRightMargin: UIViewAutoresizing { get }  public static var flexibleTopMargin: UIViewAutoresizing { get }  public static var flexibleHeight: UIViewAutoresizing { get }  public static var flexibleBottomMargin: UIViewAutoresizing { get }}
  • 在swift中直接使用  let resize = [. flexibleLeftMargin, . flexibleWidth...]

  • 在swift中全局变量和存储属性都被保证只初始化一次,所以用来当作OC里面的#define定义的常量。同时swift全局函数可以当作OC里#define定义的复杂宏(类似函数)

  • swift中的条件编译 自定义编译符的方法(例如: DEBUG_LOGGING)

  • 首先在project 的设置里面设置swift -D DEBUG_LOGGING to set the DEBUG_LOGGING

  • 然后使用 #if DEBUG_LOGGING // 操作 #endif

1234567891011 #if arch(arm) || arch(arm64)#if swift(>=3.0)print("Using Swift 3 ARM code")#elseprint("Using Swift 2.2 ARM code")#endif#elseif arch(x86_64)print("Using 64-bit x86 code.)#elseprint("Using general code.")#endif
  • swift中使用指针的方式

  • 使用inout方式 &变量

  • 使用UnsafePointer或者UnsafeMutablePointer

例如这个函数接受的参数可以传许多种

123456 let x: Float = 0func takesAPointer(_ p: UnsafePointer!) {// ...}takesAPointer(&x)takesAPointer([0.0,1.0])
  • 在swift中申明oc中的包含可选实现方法的协议时需要在协议和方法前都标记objc

12345 @objc public protocol MySwiftProtocol {// 必须实现  func requiredMethod()  @objc optional func optionalMethod()}
  • 将oc的方法或者属性使用NS_SWIFT_NAME()可以为他们在swift中命一个别名

  • 将oc的方法或使用 NS_SWIFT_UNAVAILABLE()可以让他在swift中不可用