属性是封装数据的方式(参见第6条)。尽管从技术上说,分类里也可以声明属性,但这种做法还是要尽量避免。
原因在于,除了“class-continuation分类”
(参见第27条)之外,其他分类都无法向类中新增实例变量,因此,它们无法把实现属性所需的实例变量合成出来。如果我们在分类中声明了一个friends
属性。会提示我们
warning: property 'friends' requires method 'friends' to be defined - use @dynamic or provide a method implementation in this category [-Wobjc-property-implementation]
warning: property 'friends' requires method 'setFriends:' to be defined - use @dynamic or provide a method implementation in this category [-Wobjc-property-implementation]
说明系统没有为我们自动合成属性的set
和get
方法。我们要自己在分类中去实现,可以把存取方法声明为@dynamic
, 也就是说,这些方法等到运行期再提供,编译器目前是看不见的。如果决定使用消息转发机制(参见第12条)在运行期拦截方法调用,并提供其实现,那么或许可以采用这种做法。
当然我们也可以使用关联对象的方法。但是还是不建议我们在分类中定义封装数据的属性。
正确做法是把所有属性都定义在主接口里。类所封装的全部数据都应该定义在主接口中,这里是唯一能够定义实例变量(也就是数据)的地方。而属性只是定义实例变量及相关存取方法所用的“语法糖”,所以也应遵循同实例变量一样的规则。至于分类机制,则应将其理解为一种手段,目标在于扩展类的功能,而非封装数据。
但是有时候,只读属性(readonly)可以在分类中使用,但是我们要手动实现它的get
方法。当然我们不建议搞特殊。最好还是在主接口中声明。然后在分类中声明一个获取方法,来获取数据。
要点
- 把封装数据所用的全部属性都定义在主接口里。
- 在
“class-contimiation分类”
之外的其他分类中,可以定义存取方法,但尽量不要定义属性。