Swift: 如何使用预处理器为*低于*某个iOS版本添加扩展方法?

Swift: How to use preprocessors to add an extension method for *below* a certain iOS version?

我正在尝试 "polyfill" NSManagedObjects init(context:) 方法 iOS9。有没有办法对 belowiOS10 进行预处理器可用性检查?

这是否有意义,或者是否会出现链接冲突问题b/c我们不知道用户将使用哪个iOS版本运行?

请注意,这与 @available(iOS 8, *) 宏不同。我想要相反的东西:如果不可用。或者更准确地说,我想要 @available(iOS <10.0, *)、"available if iOS is less than 10.0"

这是您要找的吗?

@available(iOS, deprecated: 10.0)

不过,它在 Swift 中被称为 Attributes。没有预处理器。

试试这个语法来显示警告:

@available(iOS, introduced: 8.0, deprecated: 10.0)

或者这个是为了防止在当前 iOS 目标中使用它:

@available(iOS, introduced: 8.0, obsoleted: 10.0)

您甚至可以提供自定义消息:

@available(iOS, introduced: 8.0, obsoleted: 10.0, message: "This is obsoleted")

我认为仅仅依靠 Swift 中的 compile-time 操作是不可能的。如果您关心,通过在 Objective-C 中编写一个定义该方法的扩展,您的实现将在平台存在时覆盖它,因此这可能是一个可行且简单的解决方案。

如果您只希望您的实现仅在没有本机实现时才启动,您应该也可以在 Objective-C 中轻松地做到这一点。首先,我有两个警告:

  1. 您正在修改 CoreData,它本身已经很动态了,我不确定它会如何反应。
  2. 我无法验证我在这台计算机上所说的任何内容,所以如果它不起作用请发表评论:)

在桥接 header 中,确保编译器知道 init(context:) 可用,无论 iOS 版本如何:

@interface NSManagedObject ()
+(void)initialize;
-(instancetype)initWithContext:(NSManagedObjectContext* _Nonnull)ctx;
@end

+initialize 类别中声明的方法的执行独立于 class 本身是否有 +initialize 方法或任何其他类别是否有。

实施将如下所示:

@implementation NSManagedObject ()

static id initWithContext(NSManagedObject* self, SEL sel, NSManagedObjectContext* context) {
    // your implementation goes here
}

+(void)initialize {
    SEL init = @selector(initWithContext:);
    if (![self instancesRespondToSelector:init]) {
        class_addMethod(self, init, (IMP)initWithContext, "@:@");
    }
}

@end

我认为这相当简单:检查 NSManagedObject 是否支持 initWithContext:,如果不支持,请添加您自己的实现。您不需要提供 initWithContext:.

的显式实现