避免在Category中出现属性
属性是一种数据的封装形式,理论上来说,你也可以在Category中声明属性,但是我们应该尽量避免这样做。
因为这样做,Category是不会生成任何实例变量的,当然也不能够由实例变量去合成一个属性变量的。
比如我们创建一个Friend的Person分类,用来处理Person类中关于“Friend”的逻辑内容,如果你像下面这样声明:
#import "Person.h"
@interface Person (Friendship)
@property (nonatomic, strong) NSArray *friends;
- (void)addFriend:(Person *)aFreind;
@end
编译会得到这样的警告:
Property 'friends' requires method 'setFriends:' to be defined - use @dynamic or provide a method implementation in this category
这个有点“莫名其妙”的警告的意思是无法为这个属性生成实例变量,从而也不能合成一个属性变量。
如果你非要在分类中声明这个friends的数组属性,那么需要将friend声明为@dynamic或者自己为实例变量提供setter和getter方法。将属性声明为dynamic意思就是告诉编译器,自己会在runtime生成setter和getter方法,让编译器给我们放行通过:
@implementation Person (Friendship)
@dynamic friends;
@end
另一种方法如下:
static const char *kFriendProperty = "friend";
@implementation Person (Friendship)
//@dynamic friends;
- (NSArray *)friends
{
return objc_getAssociatedObject(self, kFriendProperty);
}
- (void)setFriends:(NSArray *)friends
{
objc_setAssociatedObject(self, kFriendProperty, friends, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
虽然通过上面的方法我们可以理论上的实现为Category增加属性,但是这仍然是不推荐的,如果属性是像NSMutableArray那样可变的,那么处理起来就可能会更加麻烦。因此,最理想的方法还是将属性和数据的封装放到主类中。
使用类扩展来隐藏实现细节
其实类扩展,听着很高大上,只不过是下面代码中注释的部分。
#import "Person+Friendship.h"
#import <objc/runtime.h>
//@interface Person()
//
//@end
static const char *kFriendProperty = "friend";
@implementation Person (Friendship)
- (NSArray *)friends
{
return objc_getAssociatedObject(self, kFriendProperty);
}
这个学过iOS的应该都知道一点其用法,主要是用来声明一些隐藏的变量用的。
例子如下:
#import "Person+Friendship.h"
#import <objc/runtime.h>
@interface Person()
{
NSString *_aString;
}
@end
static const char *kFriendProperty = "friend";
@implementation Person (Friendship)
//@dynamic friends;
- (NSArray *)friends
{
return objc_getAssociatedObject(self, kFriendProperty);
}
此时aStirng就作为一个“隐藏”的实例变量而不用对外暴露。
多的不多做介绍,主要总结如下
-
1 通过类扩展可以向类中新增实例变量。
-
2 如果某属性在主接口中声明为“readonly”,而类的内部又要用设置方法修改此属性,那么就在类扩展中将其扩展为“readwrite”。
-
3 如果想让类所遵循的协议不暴露在主接口,可以在类扩展中进行声明。
网友评论