如何创建符合Swift和Objective-C之间共享协议的类方法? [英] How to create class methods that conform to a protocol shared between Swift and Objective-C?

查看:94
本文介绍了如何创建符合Swift和Objective-C之间共享协议的类方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近一直在学习Swift.

I've been learning Swift lately.

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

I decided to write a hybrid Swift/Objective-C app that did compute-intensive tasks using the same algorithm implemented in both languages.

程序会计算大量素数.

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

I defined a protocol that both the Swift and the Objective-C version of the calculate object are supposed to conform to.

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

The objects are both singletons, so I created a typical singleton access method in 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版本的类完全按照上面定义的方法实现,不用担心.

The Objective-C version of the class implements the methods exactly as defined above, no worries.

快速版本具有一种方法:

The swift version has a method:

  class func sharedInstance() -> CalcPrimesProtocol

但是,如果我将该方法作为协议的必需方法,则会出现编译器错误类型" CalcPrimesSwift不符合协议"CalcPrimesProtocol".

However, if I make that method a required method of the protocol, I get a compiler error "Type "CalcPrimesSwift does not conform to the protocol 'CalcPrimesProtocol'.

但是,如果我在协议中将单例类方法sharedInstance标记为可选,则它可以工作,并且可以在我的Swift类或Objective-C类上调用该方法.

If I mark the singleton class method sharedInstance as optional in the protocol, however, it works, and I can invoke that method on either my Swift class or my Objective-C class.

我想念我的Swift类方法的定义有些微妙吗?鉴于我能够在Swift类或Objective-C类上调用sharedInstance()类方法,因此这似乎不太可能.

Did I miss some subtlety in the definition of my Swift class method? It seems unlikely, given that I'm able to invoke the sharedInstance() class method on either my Swift class or my Objective-C class.

您可以从Github下载该项目,并查看是否需要.它称为 SwiftPerformanceBenchmark . (链接)

You can download the project from Github and check it out if you'd like. It's called SwiftPerformanceBenchmark. (link)

推荐答案

在Objective-C中,我们总是在传递指针,而指针始终可以是nil.许多Objective-C程序员利用了以下事实:向nil发送消息没有任何作用,并返回了0/nil/NO. Swift处理nil的方式完全不同.对象要么存在(从不nil),要么不知道它们是否存在(这是Swift可选对象起作用的地方).

In Objective-C, we were always passing around pointers, and pointers could always be nil. Lots of Objective-C programmers made use of the fact that sending a message to nil did nothing and returned 0/nil/NO. Swift handles nil entirely differently. Objects either exist (never nil), or it is unknown whether or not they exist (which is where Swift optionals come into play).

因此,在Xcode 6.3之前,这意味着任何使用Objective-C代码的Swift代码都必须将所有对象引用视为Swift可选对象.关于Objective-C的语言规则,没有什么能阻止对象指针为nil.

Previous to Xcode 6.3, this therefore meant that any Swift code that used any Objective-C code would have to treat all object references as Swift optionals. Nothing about Objective-C's language rules prevented an object pointer from being nil.

这对于使用Swift中的Objective-C协议,类等意味着什么呢?我们必须在非完美解决方案之间进行选择.

What this meant for using Objective-C protocols, classes, etc., from Swift is that it was a massive mess. We had to choose between to non-perfect solutions.

给出以下Objective-C协议:

Given the following Objective-C protocol:

@protocol ObjCProtocol <NSObject>

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

@end

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

We can either accept the method definition as containing implicitly unwrapped optionals:

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

这可以使生成的代码更整洁(我们不必在体内拆开包装),但是,我们始终会面临发现nil,同时打开可选包装"错误的风险.

This makes the resulting code cleaner (we never have to unwrap within the body), however we will always be at risk of the "found nil while unwrapping optional" error.

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

Or alternatively, we can define the method as being a true optional:

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

但这会给我们留下很多混乱的代码.

But this leaves us with a lot of mess unwrapping code.

Xcode 6.3解决了此问题,并为Objective-C代码添加了可空性注释".

Xcode 6.3 fixes this problem and adds "Nullability Annotations" for Objective-C code.

两个新引入的关键字是nullablenonnull.这些用于与您声明Objective-C代码的返回类型或参数类型相同的地方.

The two newly introduced keywords are nullable and nonnull. These are used in the same place you're declaring the return type or parameter type for your Objective-C code.

- (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.

In addition to these two annotation keywords, Xcode 6.3 also introduces a set of macros that makes it easy to mark large sections of Objective-C code as nonnull (files with no annotations at all are effectively assumed as nullable). For this, we use NS_ASSUME_NONNULL_BEGIN at the top of the section and NS_ASSUME_NONNULL_END at the bottom of the section we wish to mark.

例如,我们可以将整个协议包装在该宏对中.

So, for example, we could wrap your entire protocol within this macro pair.

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博客中的该条目记录了).

This has the same effect as marking all of the pointer parameters and return types as nonnull (with a few exceptions, as this entry in Apple's Swift blog makes note of).

符合Objective-C协议的Swift类必须将该协议中的所有Objective-C类型都视为可选.

A Swift class that conforms to an Objective-C protocol must treat any Objective-C types in that protocol as optionals.

为了弄清楚这一点,我创建了以下Objective-C协议:

In trying to figure this out, I created the following Objective-C protocol:

@protocol ObjCProtocol <NSObject>

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

@end

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

And then, created a Swift class which inherited from NSObject and declared itself as conforming to this ObjCProtocol.

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

I then proceeded to type these method names and let Swift autocomplete the methods out for me, and this is what I got (I put in the method bodies, the rest if autocomplete):

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传递.

Now, we can use regular optionals (with the ?) instead of these automatically unwrapped optionals if we want. The compiler is perfectly happy with either. The point is though that we must allow for the possibility of nil, because Objective-C protocols cannot prevent nil from being passed.

如果此协议是在Swift中实现的,我们将选择返回类型是否为可选,而Swift将阻止我们将nil返回至未定义非可选返回类型的方法.

If this protocol were implemented in Swift, we'd get to choose whether the return type is optional or not and Swift would prevent us from returning nil to a method that didn't define a non-optional return type.

这篇关于如何创建符合Swift和Objective-C之间共享协议的类方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆