相信大多数开发者第一次接触到 Objective-C 的 @dynamic 都是在和 Core Data 打交道的时候。Objective-C 中的 @dynamic 和 Swift 中的 dynamic 关键字完全是两回事。在 Objective-C 中,如果我们将某个属性实现为 @dynamic 的话,就意味着告诉编译器我们不会在编译时就确定这个属性的行为实现,因此不需要在编译期间对这个属性的 getter 或/及 setter 做检查和关心。这是我们向编译器做出的庄严承诺,表示我们将在运行时来提供这个属性的存取方法 (当然相应地,如果在运行时你没有履行这个承诺的话,应用就会挂给你看)。

所有的 Core Data Model 类都是 NSManagedObject 的子类,它为我们实现了一整套的机制,可以利用我们定义的 Core Data 数据图和关系在运行时动态生成合适的 getter 和 setter 方法。在绝大多数情况下,我们只需要使用 Xcode 的工具自动生成 NSManagedObject 的子类并使用就行了。在 Objective-C 中一个典型的 NSManagedObject 子类的样子是这样的:

// MyModel.h
@interface MyModel : NSManagedObject

@property (nonatomic, copy) NSString * title;

@end

// MyModel.m
#import "MyModel.h"
@implementation MyModel

@dynamic title;

@end

很遗憾,Swift 里是没有 @dynamic 关键字的,因为 Swift 并不保证一切都走动态派发,因此从语言层面上提供这种动态转发的语法也并没有太大意义。在 Swift 中严格来说是没有原来的 @dynamic 的完整的替代品的,但是如果我们将范围限定在 Core Data 的话就有所不同。

Core Data 是 Cocoa 的一个重要组成部分,也是非常依赖 @dynamic 特性的部分。Apple 在 Swift 中专门为 Core Data 加入了一个特殊的标注来处理动态代码,那就是 @NSManaged。我们只需要在 NSManagedObject 的子类的成员的字段上加上 @NSManaged 就可以了:

class MyModel: NSManagedObject {

    @NSManaged var title: String

}

这时编译器便不再会纠结于没有初始化方法实现 title 的初始化,而在运行时对于 MyModel 的读写也都将能利用数据图完成恰当的操作了。

另外,在通过数据模型图创建 Entity 时要特别注意在 Class 中指定类型名时必须加上 app 的 module 名字,才能保证在代码中做类型转换时不发生错误。

最后要指出一点,Apple 在文档中指出 @NSManaged 是专门用来解决 Core Data 中动态代码的问题的,因此我们最好是遵守这个规则,只在 NSManagedObject 的子类中使用它。但是如果你将 @NSManaged 写到其他的类中,也是能够编译通过的。在这种情况下,被标记的属性的访问将会回滚到 Objective-C 的 getter 和 setter 方法。也即,对于一个叫做 title 的属性,在运行时会调用 titlesetTitle: 方法。行为上来说和以前的 @dynamic 关键字是一样的,我们当然也可以使用 Objective-C 运行时来提供这两个方法,但是要注意的是这么做的话我们就必须对涉及到的类和方法标记为 @objc。我并不推荐这样做,因为你无法知道这样的代码在下一个版本中是否还能工作。