编写高质量代码的52个有效方法

52个有效方法(26) - 勿在分类中声明属性

2018-09-05  本文已影响11人  SkyMing一C
#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 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]
*/
使用Runtime给分类添加属性
@interface NSObject (EOC_CX)
/**
 *  为每一个对象添加一个name属性
 */
@property (nonatomic,copy) NSString *name;
/**
 *  为每个对象添加一个View属性
 */
@property (nonatomic,strong) UIView *booksView;
/**
 *   为每个对象添加一个是否被选中属性
 */
@property(nonatomic, assign) BOOL isSelected;

@end
#import "NSObject + EOC_CX .h"
#import <objc/runtime.h>
// 使用对象关联需引入#import <objc/runtime.h>头文件
@implementation NSObject (EOC_CX)
// 用一个字节来存储key值,设置为静态私有变量,避免外界修改
static void *nameKey;
- (void)setName:(NSString *)name
{
    // 将某个值与某个对象关联起来,将某个值存储到某个对象中
    objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name
{
     return objc_getAssociatedObject(self, &nameKey);
}
static void *booksViewKey;
- (void)setBooksView:(UIView *) booksView
{
    objc_setAssociatedObject(self, &booksViewKey, booksView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIView *) booksView
{
    return objc_getAssociatedObject(self, &booksViewKey);
}
//将bool类型转变成NSNumber类型来进行添加属性 这样储存策略为OBJC_ASSOCIATION_COPY_NONATOMIC
static void *isSelectedKey;
- (void)setIsSelected:(BOOL)isSelected {
    objc_setAssociatedObject(self, &isSelectedKey, @(isSelected), OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (BOOL)isSelected {
    return [((NSNumber *) objc_getAssociatedObject(self, &isSelectedKey)) boolValue];
}
@end
#import "NSObject + EOC_CX .h"
#import <objc/runtime.h>
// 使用对象关联需引入#import <objc/runtime.h>头文件
@implementation NSObject (EOC_CX)
- (void)setName:(NSString *)name
{
    objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name
{
     return objc_getAssociatedObject(self, _cmd);
    //_cmd 代替了 &nameKey 或者 @selector(name).
}
- (void)setBooksView:(UIView *)booksView
{
    objc_setAssociatedObject(self, @selector(booksView), booksView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIView *) booksView
{
    return objc_getAssociatedObject(self, _cmd);
    //_cmd 代替了 &booksKey 或者 @selector(booksView).
}
//将bool类型转变成NSNumber类型来进行添加属性 这样储存策略为OBJC_ASSOCIATION_COPY_NONATOMIC
- (void)setIsSelected:(BOOL)isSelected {
    objc_setAssociatedObject(self, @selector(isSelected), @(isSelected), OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (BOOL)isSelected {
    return [objc_getAssociatedObject(self, _cmd) boolValue];
    //_cmd 代替了 &isSelectedKey 或者 @selector(isSelected).
}
@end

但是这样的形式,虽然可行,书中并不推荐,因为要把相似的代码写很多遍,而且在内存管理问题上容易出错,因为我们在为属性实现存取方法时,经常会忘记遵从其内存管理语义。比方说,你可能通过属性特质(attribute)修改了某个属性的内存管理语义。而此时还要记得,在设置方法中也得修改设置关联对象时所用的内存管理语义才行。<要保持属性声明中的内存管理的语义与runtime中传入的语义参数保持一致>。

要点
  1. 把封装数据所用的全部属性都定义在主接口中。

  2. 在“class-continuation分类”之外的其他分类中,可以定义存取方法,但尽量不要定义属性。

上一篇下一篇

猜你喜欢

热点阅读