避免在类别(category)中定义属性(@property)

原创 2016年08月29日 18:08:02

property 是包装数据的一种办法.尽管技术上可以实现在category里面声明一个property,但是应该尽量避免这样做.理由是,除了class延续类别外,是不可能用一个category对class添加一个实例变量.因此对于category同样也不可能合成一个实例变量去支持property.我们来切割下本来是实现person的class.你可能需要一个关于友谊的category声明方法,来操作关于这个person朋友的列表.在不知道描述的问题前,你也可以把property的friends列表放到友谊category里面.想这样:

#import <Foundation/Foundation.h>
@interface EOCPerson : NSObject
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
- (id)initWithFirstName:(NSString*)firstName andLastName:(NSString*)lastName;
@end
@implementation EOCPerson
// Methods
@end

@interface EOCPerson (Friendship)
@property (nonatomic, strong) NSArray *friends;
- (BOOL)isFriendsWith:(EOCPerson*)person;
@end

@implementation EOCPerson (Friendship)
// Methods
@end

假如你进行编译,可是你将会收到编译器警告后结束warning: property 'friends' requires method 'friends' to bedefined - use @dynamic or provide a method implementation inthis category [-Wobjc-property-implementation]warning: property 'friends' requires method 'setFriends:' to bedefined - use @dynamic or provide a method implementation inthis category [-Wobjc-property-implementation]这个稍微有点含糊的警告意思是说不能用category合成一个实例变量,并且因此property需要有一个accessor方法来实现在category中.或者,可以声明accessor方法@dynamic,意味着你声明的变量将在运行时有效,但是编译时是看不到的.这可能发生的情况是如果你使用消息转发机制来拦截这个方法并且在运行时提供实现.

为了避免category不能合成实例变量的问题,你可以使用关联的对象.例如,你可能需要去实现下面category内的关联:

#import <objc/runtime.h>
static const char *kFriendsPropertyKey = "kFriendsPropertyKey";
@implementation EOCPerson (Friendship)

- (NSArray*)friends {
 return objc_getAssociatedObject(self, kFriendsPropertyKey);
}

- (void)setFriends:(NSArray*)friends {
 objc_setAssociatedObject(self,kFriendsPropertyKey, friends,OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

这样不是很完美的解决办法.是有大量的引用和容易出现内存管理错误.因为很容易就会忘记这个property是像这样实现的.例如,你可以用改变property的属性来修改内存管理语义.但是你同样需要去记得改变内存管理语义在setter内关联的对象.尽管这是一个不错的解决方案,但是我不推荐.

同样,你可能希望这个实例变量可以支持这个friends数组是一个可变数组. 你可以带一个可变拷贝,但是这样又会是另外一种无尽的混乱开始进入你的代码基础.因此property在主要接口定义比在category里面定义要干净的多.

在这个示例中,正确的解决办法就是把所有的property定义放到主接口声明.所有的数据封装在一个主接口定义的类中,主接口是唯一可以声明实例变量(数据)的地方.因为他们仅仅是定义一个实例变量和相关语法糖访问器方法,property受到相同的规则.category应该被想到用做一个类的方法扩展来扩展功能,而不是封装数据.也就是说,有些时候只读属性可以被成功的用在category中.例如,你可能想建立一个NSCalendar的category来返回一个包含月份字符串的数组.因为这个方法访问任何数据,并且这个property不支持一个实例变量,你可以实现这个category像这样:

@interface NSCalendar (EOC_Additions)
@property (nonatomic, strong, readonly) NSArray *eoc_allMonths;
@end
@implementation NSCalendar (EOC_Additions)
- (NSArray*)eoc_allMonths {
 if ([self.calendarIdentifier
 isEqualToString:NSGregorianCalendar])
 {
 return @[@"January", @"February",
 @"March", @"April",
 @"May", @"June",
 @"July", @"August",
 @"September", @"October",
 @"November", @"December"];
 } else if ( /* other calendar identifiers */ ) {
 /* return months for other calendars */
 }}
@end

property所支持的实例变量将不会自动合成,因为所有所需的方法(这里只有一个只读方法)需要被实现.因此,编译器会发出警告.然而,即使在这种情况下,一般最好避免使用property. property的用意就在于依赖class对数据的支持.property是封装数据,在本例中,你在category内将替换声明方法为检索列表中的月份.

@interface NSCalendar (EOC_Additions)
- (NSArray*)eoc_allMonths;
@end
需要记住的事情1.把所有封装数据的property声明都在住接口中定义.2.在category中希望使用访问器方法来声明property,除非他是一个类延续(class-continuation)category
版权声明:本文为博主原创文章,未经博主允许不得转载。

消除Xcode iOS项目的警告

原文:消除Xcode iOS项目的警告 如果你有强迫症,看到项目中那么多警告肯定特别心烦 第一种警告样式 ld: warning: directory not found for...
  • Nigelr
  • Nigelr
  • 2016年07月02日 14:47
  • 6655

iOS 在分类中添加属性

OC 对类的宽展有三中方式,分类,延展,继承,延展和继承不必多说。今天就说下如何给分类添加属性。我们知道OC比较爽的就是可以添加分类,在分类中添加自己喜欢的方法。可能有人会问了,不能添加属性啊 ,其实...
  • wang1018960145
  • wang1018960145
  • 2016年04月26日 16:58
  • 975

[iOS]iOS在分类中声明属性的方法(不提倡使用)以及关联对象介绍

今天只是想看看能不能在分类中声明属性,发现果然可以,不过最好还是不要使用。 首先在一个分类里声明一个属性,执行。 没有问题 然后其它类中引入这个分类,访问我生命的这个属性。 报错了 没有set...
  • a2331046
  • a2331046
  • 2015年12月25日 20:32
  • 686

iOS中为category添加@property支持

Objective-C runtime提供了Associative References支持,也就是说每一个对象都有一个可选的dictionary字典,我们可以向其添加key/value对。 ...
  • u010306571
  • u010306571
  • 2014年05月06日 18:01
  • 1022

避免在类别(category)中定义属性(@property)

property 是包装数据的一种办法.尽管技术上可以实现在category里面声明一个property,但是应该尽量避免这样做.理由是,除了class延续类别外,是不可能用一个category对cl...
  • yidu_blog
  • yidu_blog
  • 2016年11月09日 16:39
  • 825

OC 自动生成分类属性方法

分类属性方法自动生成编码全过程。
  • u010124617
  • u010124617
  • 2016年08月23日 16:46
  • 778

iOS之属性定义和内存管理

当把属性的语义特性分别声明为assign,retain,copy时, 自动生成setter, getter方法内部实现 假定有一个车类Car.h中定义三个属性 @property (nonatomic...
  • LVXIANGAN
  • LVXIANGAN
  • 2016年02月24日 12:14
  • 1153

关于@property定义的属性和大括号里定义的属性的区别

@propery修饰的属性可以被外部文件直接访问,而大括号里面声明的属性是不能被外部文件访问的。 原理:@property修饰的属性其实是自动生成该属性以及该属性的getter,setter方法的声明...
  • u011010305
  • u011010305
  • 2016年07月01日 15:36
  • 1264

iOS 中的类属性

转自:iOS 知识小集从Xcode 8开始,LLVM已经支持Objective-C显式声明类属性了,这是为了与Swift中的类属性互操作而引入的。在WWDC 2016 What’s New in LL...
  • u011619283
  • u011619283
  • 2016年11月12日 12:08
  • 3743

iOS 分类添加属性

iOS 分类添加属性 头文件 #import @interface NSObject (Extend) @property (nonatomic, copy) NSString *testS...
  • scaulaughter
  • scaulaughter
  • 2018年01月25日 21:33
  • 18
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:避免在类别(category)中定义属性(@property)
举报原因:
原因补充:

(最多只允许输入30个字)