美文网首页编写高质量代码的52个有效方法
52个有效方法(26) - 勿在分类中声明属性

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

作者: SkyMing一C | 来源:发表于2018-09-05 17:34 被阅读11次
    • 分类机制,应将其理解为一种手段,目标在于扩展类的功能,而非封装数据。

    • 分类是用于给原有类添加方法的,因为分类的结构体指针中,没有属性列表,只有方法列表。所以< 原则上讲它只能添加方法, 不能添加属性(成员变量),实际上可以通过objc/runtime添加属性 >

    • 分类中的可以写@property, 但不会生成setter/getter方法, 也不会生成实现以及私有的成员变量(编译时会报警告)

    #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分类”之外的其他分类中,可以定义存取方法,但尽量不要定义属性。

    相关文章

      网友评论

        本文标题:52个有效方法(26) - 勿在分类中声明属性

        本文链接:https://www.haomeiwen.com/subject/xjsawftx.html