美文网首页编写高质量代码的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) - 勿在分类中声明属性

    分类机制,应将其理解为一种手段,目标在于扩展类的功能,而非封装数据。 分类是用于给原有类添加方法的,因为分类的结构...

  • 《Effective Objective-C 2.0 》 阅读笔

    第26条:勿在分类中声明属性 要点 把封装数据所用的全部属性都定义在主接口里。 在“Class-continuat...

  • 第26条:勿在分类中声明属性

    属性是封装数据的方式。从技术上说,分类里也可以声明属性,但这个做法应该尽量避免,原因在于,除了“class-con...

  • ios 分类中如何添加数据成员

    分类中可以声明属性,因为声明属性其实就是声明设置方法和获取方法。但是分类中不可以合成属性,因为合成属性其实就是创建...

  • 看书笔记26-30

    二十六:勿在分类中声明属性 把封装数据所用的全部属性都定义在主接口里。在"class-continuation分类...

  • 勿在分类中声明属性--Effective笔记

    属性是封装数据的方式。虽然分类也可以声明属性,但这种做法还是要尽量避免。因为分类中一般是无法新增实例变量(可用关联...

  • 关联属性

    分类中不可以添加成员变量 分类中可以声明属性,分类中声明属性只会生成方法的声明,但不会生成成员变量和方法的实现。 ...

  • 6-关联对象

    属性 在类中声明一个属性会生成成员变量并声明和实现对应的set,get方法 在分类中声明一个属性只会声明对应的se...

  • 分类中声明属性

    分类(category).h中: .m中: 这种分类如果声明属性, 编译器会报警告。意思是此分类无法生成属性相关实...

  • iOS 分类中添加属性

    注意 分类主要是给原类添加方法的,分类的结构指针中只有方法列表,没有属性列表。所以原则上分类里面最好不要声明属性 ...

网友评论

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

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