在Objective-C中,@property声明(参见第10章)包含了一个内存管理策略语句,后面跟着相应的setter访问器方法。意识到这一点并知道如何将这种策略语句转换为Swift是很有必要的。
比如,之前曾提到过UIViewController会保持其view(其主视图)。我是怎么知道的呢?这是@property声明告诉我的:
@property(null_resettable, nonatomic, strong) UIView *view;
关键字strong表示setter会保持接收到的UIView对象。这个声明转换为Swift后并不会向变量添加任何特性:
var view: UIView!
在Swift中,默认做法是指向引用对象类型的变量是个强引用,即持久化引用。这意味着它会保持对象。这样就可以从这个声明中推断出,UIViewController会保持其view。
对于Cocoa属性来说,其内存管理策略有:
strong,retain(Swift中没有等价之物)
默认值。这两个关键字彼此等价;retain源自ARC出现之前。为该属性赋值会保持接收到的值并释放现有值。
copy(Swift中没有等价之物,或@NSCopying)
与strong和retain相同,只不过setter会通过向其发送copy复制接收到的值;进来的值必须是使用了NSCopying的对象类型,从而确保这么做是可行的。副本(已经增加了保持计数值)会成为新值。
weak(Swift weak)
一个ARC弱引用。接收到的值不会被保持,不过如果在我们不知情的情况下销毁了,那么ARC会将nil替换为该属性的值,其类型必须是声明为var的Optional。
assign(Swift unowned(unsafe))
无内存管理。该策略源自ARC出现之前,本身就是不安全的(因此,转换为Swift后会有额外的unsafe警告):如果被引用的对象销毁了,那么该引用就会变成一个野指针,后面如果使用它就会导致应用崩溃。
你可能很想了解关于copy策略的一些内容,因为之前并没有介绍过。当不变类有可变子类时(比如,NSString与NSMutableString,以及NSArray与NSMutableArray;参见第10章),Cocoa就会使用该策略。它解决了setter调用者传递可变子类对象的风险。稍微想一下就知道这是可能的,因为根据多态的替换法则(参见第4章),在需要一个类的实例时,我们可以传递其子类的实例。不过,这么做可能不太好,因为现在调用者会保持着对传递进来的值的引用,因为它是可变的,因此后面可能会在我们不知情的情况下发生变化。为了防止这种情况的发生,setter会调用传递进来对象的copy;这会创建新的实例,它与所提供的对象不同,并且属于不可变类。
在Swift中,这个问题基本不会出现在字符串与数组上,因为在Swift这一边,它们是值类型(结构体),赋值时会被复制,然后才作为参数传递,或作为返回值被接收。这样,Cocoa的NSString与NSArray属性声明在转换为Swift的String与Array属性声明时,它们并不需要与Objective-C copy对应的任何特殊标记。不过,不会自动从Swift结构体桥接的Cocoa类型是会显示一个标记的,即@NSCopying。比如,在Swift中,UILabel的attributedText属性声明如下所示:
@NSCopying var attributedText: NSAttributedString?
NSAttributedString有一个可变子类NSMutableAttributedString。你可能已经将这个特性字符串配置为了NSMutableAttributedString,现在要将UILabel的attributedText赋给它。UILabel并不希望你保持一个对该可变字符串的引用并修改它,因为这会在不使用setter的情况下改变属性值。这样,它会复制传递进来的值,确保它是一个不同的可变NSAttributedString。
你也可以在自己的代码中这么做,并且也愿意这么做。如果类中有一个NSAttributed-String实例属性,那么可以将其标识为@NSCopying,对于其他的可变/不变成员对也是类似的,比如,NSIndexSet、NSParagraphStyle及NSURLRequest等。只提供@NSCopying标识即可;Swift会强制应用copy策略,并且在为该属性赋值时进行实际的复制动作。
有时,你希望自己的类拥有改变属性值的能力,同时又想防止外部传递进来可变的值,那么就需要在其前面加上一个私有的计算属性门面,它会将其转换为相应的可变类型:
class StringDrawer { @NSCopying var attributedString : NSAttributedString! private var mutableAttributedString : NSMutableAttributedString! { get { if self.attributedString == nil {return nil} return NSMutableAttributedString( attributedString:self.attributedString) } set { self.attributedString = newValue } }}
@NSCopying只能用于类的实例属性,结构体与枚举是不行的,并且只能用在使用了Foundation的场合中,这是因为NSCopying协议定义在Foundation中,标记为
@NSCopying的变量类型都需要使用该协议。