如何创建符合 Swift 和 Objective-C 之间共享协议的类方法?

2024-04-15

我最近一直在学习 Swift。

我决定编写一个混合 Swift/Objective-C 应用程序,使用两种语言实现的相同算法来执行计算密集型任务。

该程序计算大量素数。

我定义了一个 Swift 和 Objective-C 版本的计算对象都应该遵守的协议。

这些对象都是单例的,所以我在 Objective-C 中创建了一个典型的单例访问方法:

+ (NSObject <CalcPrimesProtocol> *) sharedInstance;

整个协议如下所示:

#import <Foundation/Foundation.h>
@class ComputeRecord;

typedef void (^updateDisplayBlock)(void);
typedef void (^calcPrimesCompletionBlock)(void);

    @protocol CalcPrimesProtocol <NSObject>

- (void) calcPrimesWithComputeRecord: (ComputeRecord *) aComputeRecord
              withUpdateDisplayBlock: (updateDisplayBlock) theUpdateDisplayBlock
                  andCompletionBlock: (calcPrimesCompletionBlock) theCalcPrimesCompletionBlock;

    @optional //Without this @optional line, the build fails.
    + (NSObject <CalcPrimesProtocol> *) sharedInstance;

    @end

该类的 Objective-C 版本完全按照上面定义的方式实现方法,不用担心。

swift版本有一个方法:

  class func sharedInstance() -> CalcPrimesProtocol

但是,如果我将该方法设为协议的必需方法,则会收到编译器错误“Type”CalcPrimesSwift does not follow the protocol 'CalcPrimesProtocol'。

但是,如果我在协议中将单例类方法共享实例标记为可选,那么它就可以工作,并且我可以在我的 Swift 类或 Objective-C 类上调用该方法。

我是否错过了 Swift 类方法定义中的一些微妙之处?考虑到我能够在 Swift 类或 Objective-C 类上调用 sharedInstance() 类方法,这似乎不太可能。

如果您愿意,可以从 Github 下载该项目并查看。它被称为Swift 性能基准 https://github.com/DuncanMC/SwiftPerformanceBenchmark.git. (link)


在 Objective-C 中,我们总是传递指针,并且指针总是可以nil。许多 Objective-C 程序员利用了这样一个事实:发送消息到nil什么也没做并返回0/nil/NO。快速手柄nil完全不同。对象要么存在(从不nil),或者不知道它们是否存在(这就是 Swift 可选选项发挥作用的地方)。

在 Xcode 6.3 之前,这意味着任何使用 Objective-C 代码的 Swift 代码都必须将所有对象引用视为 Swift 选项。 Objective-C 的语言规则并没有阻止对象指针被nil.

对于使用 Swift 中的 Objective-C 协议、类等来说,这意味着这是一个巨大的混乱。我们必须在不完美的解决方案之间做出选择。

给定以下 Objective-C 协议:

@protocol ObjCProtocol <NSObject>

@required + (id<ObjCProtocol>)classMethod;
@required - (id<ObjCProtocol>)instanceMethod;
@required - (void)methodWithArgs:(NSObject *)args;

@end

我们可以接受包含隐式解包选项的方法定义:

class MyClass: NSObject, ObjCProtocol {
    func methodWithArgs(args: NSObject!) {
        // do stuff with args
    }
}

这使得生成的代码更干净(我们永远不必在正文中解包),但是我们始终面临“解包可选时发现 nil”错误的风险。

或者,我们可以将该方法定义为真正的可选方法:

class MyClass: NSObject, ObjCProtocol {
    func methodWithArgs(args: NSObject?) {
        // unwrap do stuff with args
    }
}

但这给我们留下了很多混乱的解包代码。

Xcode 6.3 修复了这个问题,并为 Objective-C 代码添加了“Nullability Annotations”。

新引入的两个关键字是nullable and nonnull。它们用在为 Objective-C 代码声明返回类型或参数类型的同一位置。

- (void)methodThatTakesNullableOrOptionalTypeParameter:(nullable NSObject *)parameter;
- (void)methodThatTakesNonnullNonOptionalTypeParameter:(nonnull NSObject *)parameter;
- (nullable NSObject *)methodReturningNullableOptionalValue;
- (nonnull NSObject *)methodReturningNonNullNonOptionalValue;

除了这两个注释关键字之外,Xcode 6.3 还引入了一组宏,可以轻松地将大段 Objective-C 代码标记为nonnull(根本没有注释的文件被有效地假设为nullable)。为此,我们使用NS_ASSUME_NONNULL_BEGIN在该部分的顶部和NS_ASSUME_NONNULL_END在我们想要标记的部分的底部。

因此,例如,我们可以将整个协议包装在这个宏对中。

NS_ASSUME_NONNULL_BEGIN
@protocol CalcPrimesProtocol <NSObject>

- (void) calcPrimesWithComputeRecord: (ComputeRecord *) aComputeRecord
              withUpdateDisplayBlock: (updateDisplayBlock) theUpdateDisplayBlock
                  andCompletionBlock: (calcPrimesCompletionBlock) theCalcPrimesCompletionBlock;
+ (id <CalcPrimesProtocol> ) sharedInstance;

@end
NS_ASSUME_NONNULL_END

这与将所有指针参数和返回类型标记为相同的效果nonnull (但也有一些例外,正如 Apple 的 Swift 博客中的这篇文章所指出的 https://developer.apple.com/swift/blog/?id=25).


Xcode 6.3 之前的版本

符合 Objective-C 协议的 Swift 类必须将该协议中的任何 Objective-C 类型视为可选类型。

为了解决这个问题,我创建了以下 Objective-C 协议:

@protocol ObjCProtocol <NSObject>

@required + (id<ObjCProtocol>)classMethod;
@required - (id<ObjCProtocol>)instanceMethod;
@required - (void)methodWithArgs:(NSObject *)args;

@end

然后,创建一个继承自的 Swift 类NSObject并声明自己符合此规定ObjCProtocol.

然后我继续输入这些方法名称,并让 Swift 自动完成这些方法,这就是我得到的(我放入方法主体,其余的如果自动完成):

class ASwiftClass : NSObject, ObjCProtocol {
    class func classMethod() -> ObjCProtocol! {
        return nil
    }
    
    func instanceMethod() -> ObjCProtocol! {
        return nil
    }
    
    func methodWithArgs(args: NSObject!) {
        // do stuff
    }
}

现在,我们可以使用常规选项(使用?)而不是这些自动展开的选项(如果我们愿意的话)。编译器对其中任何一个都非常满意。关键是我们必须考虑到以下可能性:nil,因为 Objective-C 协议无法阻止nil从被通过。

如果这个协议在 Swift 中实现,我们可以选择返回类型是否可选,并且 Swift 会阻止我们返回nil未定义非可选返回类型的方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何创建符合 Swift 和 Objective-C 之间共享协议的类方法? 的相关文章

  • 将浮点数截断为小数点后两位的最简单方法?

    在 Swift 中 有没有办法将浮点数截断为小数点后两位 以便您可以用它执行进一步的计算 我见过的所有线程都涉及到字符串的转换 我不知道如何在数学上使用它 我尝试使用扩展 在这个论坛上找到 认为我可以在截断后转换回浮动 但我最终回到了开始的
  • Swift 2 OAuth2 LinkedIn 连接

    我将使用 Swift 2 和 Xcode 7 制作一个本机 iOS 应用程序 用户应该使用 LinkedIn 和 OAuth 2 登录 但我想知道应该如何开始设置 我对 OAuth 2 没有太多经验 有好的教程或示例应用程序吗 我看到了雷
  • OSX Swift 在默认浏览器中打开 URL

    如何使用 Swift 作为编程语言和 OSX 作为平台在系统默认浏览器中打开 URL 我发现了很多UIApplication like UIApplication sharedApplication openURL NSURL string
  • Swift - 必须由子类覆盖的类方法

    是否有一种标准方法可以在 Swift 中创建 纯虚函数 即 一个must被每个子类覆盖 如果不是 会导致编译时错误 您有两个选择 1 使用协议 将超类定义为协议而不是类 Pro 编译时检查每个 子类 不是实际的子类 是否实现了所需的方法 C
  • 在 UICollectionView 下面插入子视图

    当我在 UICollectionView 下方添加子视图时 有时子视图会显示在 UICollectionView 单元格上方 这是子视图插入代码 self collectionView insertSubview self garmentV
  • 如何在 Xcode 4.2 中创建 Shell 脚本目标?

    我想知道是否有人知道 XCode 4 1 如何创建 shell 脚本 该选项不存在 但在最新版本中可能会被称为其他名称 塔 在导航器中选择您的项目 单击 添加目标 为空目标选择 聚合 添加构建阶段 gt 添加运行脚本 单击构建阶段并编辑运行
  • iOS 视频压缩 Swift iOS 8 损坏的视频文件

    我正在尝试压缩用户相机从 UIImagePickerController 拍摄的视频 不是现有视频 而是动态视频 以上传到我的服务器 并花费少量时间来完成此操作 因此较小的尺寸是理想的选择 而不是 30 较新质量的相机为 45 mb 这是在
  • 在集合视图单元格中播放视频,就像在 Facebook 应用程序时间轴中播放的视频一样

    我想在集合视图单元格中播放视频 要求就像 Instagram 时间线 播放视频就像在 facebook 时间线中播放视频 为此 我使用了 UICollectionViewCell 我有一些图像 没有视频 现在我是来自画廊的图像 我正在使用相
  • 从 Firestore Swift 获取文档 ID

    我正在尝试从中获取文档IDFirestore通过执行这样的查询 func updateStatusInFirestore let orderid saleOrder first Orderid print orderid let setti
  • Xcode 6.4 Swift 单元测试无法编译:“GPUImage.h 未找到”“无法导入桥接标头”

    我的 Xcode 项目构建并运行良好 它有 Swift 和 Objective C 代码 它已安装 GPUImage 我向它添加了单元测试 现在它将不再编译 找不到 GPUImage h 文件 导入桥接标头失败 以下是我发现并尝试过的解决方
  • 使用 iPhone 控制蓝牙音频设备

    我正在寻找为 iPhone 编写应用程序 它将能够控制汽车中的收音机和 CD 播放器 收音机和播放器具有可用的蓝牙连接 我开始这个问题是为了获得这个地方所需的所有信息 我有几个问题 但如果您发现任何我没有要求的对我开始开发此应用程序不重要的
  • 使用 Swift 创建 NSAlert

    我有在 Objective C 中创建和 NSAlert 的代码 但我现在想在 Swift 中创建它 该警报旨在确认用户想要删除文档 我想要 删除 按钮来运行删除功能 而 取消 按钮只是为了消除警报 我怎样才能用 Swift 写这个 NSA
  • swift 中闭包和函数作为参数的区别

    我有将近 4 年的 Objective C 经验 并且是 swift 的新手 我试图从 Objective C 的角度理解 swift 的概念 所以如果我错了 请指导我 在目标 c 中 我们有块 可以稍后异步执行的代码块 这绝对是完全合理的
  • 如何将数据从一个视图传递到下一个视图?

    我正在制作一个下载排队系统来下载视频 处理下载的排队代码位于另一个视图控制器中 现在我的问题是如何将下载的 URL 传递到另一个视图而不推送到另一个视图控制器 如下所示 ViewConntroller View ViewConntrolle
  • ExpandableLabel iOS 中的“少看”

    我正在使用第三方库可扩展标签 https github com apploft ExpandableLabel实施一个see more特征 我正在寻找仅快速的解决方案 其中包含标签中的文本而不是按钮中的文本 因此这可以完美地工作 添加库并更
  • 无法识别的选择器调用静态 iOS 库中的类别方法

    我正在使用一些第三方软件来帮助使用 Xcode 4 3 2 编写 iPad 应用程序 该软件是开源的 通常经过设置 因此其代码将与开发人员为应用程序编写的任何代码一起编译 因为我在很多地方使用该软件 所以我决定将其构建为 iOS 模拟器的静
  • 如何从 UILabel 创建图像?

    我目前正在 iPhone 上开发一个简单的类似 Photoshop 的应用程序 当我想要展平图层时 标签位于良好的位置 但字体大小不佳 这是我要展平的代码 UIGraphicsBeginImageContext CGSizeMake wid
  • 如何打开实际上已关闭的 Wifi 适配器的无线电?

    我在用着托管无线网络 http managedwifi codeplex com获取我的 Wifi 适配器的无线电状态 如果收音机实际上已关闭 如何将其打开 像这样的东西 WlanClient wlanClient new WlanClie
  • Audiokit 修剪音频

    我正在制作音频编辑应用程序 我想修剪我的音频 对于音频编辑 我使用 AudioKit 框架 但我在教程和示例中找不到如何使用此框架修剪音频 异步导出允许设置开始和结束样本 https github com AudioKit AudioKit
  • 比较 Objective C 中的 NSNumber

    我是 Objective C 的初学者 对这种情况有点困惑 我有以下代码 if number1 lt number2 NSLog THE FOLLOWING NUMBER NSLog number1 NSLog IS LESS THAN N

随机推荐