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

2018-09-23  本文已影响5人  MrSYLong

属性是封装数据的方式。从技术上说,分类里也可以声明属性,但这个做法应该尽量避免,原因在于,除了“class-continuation分类”之外,其他分类都无法向类中新增实例变量,因此它们无法把实现属性所需的实例变量合成出来。

// 分类头文件添加属性,编译器会有警告信息 原因:分类无法合成与 friendsArray属性相关的实例变量
#import "EOCPerson.h"

@interface EOCPerson (FriendsShip)

@property (nonatomic, strong) NSArray *friendsArray;

@end

解决方法一:
@dynamic声明属性,在分类中为属性实现存取方法,

// 头文件
#import "EOCPerson.h"

@interface EOCPerson (FriendsShip)

@property (nonatomic, strong) NSArray *friendsArray;

- (void)setFriendsArray:(NSArray *)friendsArray;

- (NSArray *)friendsArray;

@end

// 实现文件
#import "EOCPerson+FriendsShip.h"

@implementation EOCPerson (FriendsShip)

@dynamic friendsArray;

- (void)setFriendsArray:(NSArray *)friendsArray
{
    // 这里也不能给属性赋值,需要另外一个变量,接这个friendsArray
    self.friendsArray = friendsArray;
}

- (NSArray *)friendsArray
{
    return self.friendsArray;
}

@end

解决方法二:
使用消息转发机制,在运行期拦截方法,并提供其实现。

解决方法三:
关联对象能够解决在分类中不能合成实例变量的问题。

#import <objc/runtime.h>
#import "EOCPerson+FriendsShip.h"

static const char *kFriendsPropertyKey = "kFriendsShipPropertyKey";

@implementation EOCPerson (FriendsShip)

@dynamic friendsArray;

- (void)setFriendsArray:(NSArray *)friendsArray
{
    objc_setAssociatedObject(self, kFriendsPropertyKey, friendsArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

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

@end

关联对象可行,但不太理想,相似代码写多遍,而且内存管理问题容易出错,属性的内存管理语义与设置管理对象时所用的内存管理语义一致才可以。

属性应该定义在“主接口”中,不要定义在分类中。分类机制目标是扩展类的功能,而非封装数据。

有时候只读属性可以在分类中使用。

// 头文件
#import <Foundation/Foundation.h>

@interface NSCalendar (EOC_Additions)

- (NSArray *)eoc_allMonths;

@end

// 实现文件
#import "NSCalendar+EOC_Additions.h"

@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 @[@"Jan",@"Feb",@"Mar",@"Apr",@"May",@"June",@"jul",@"Aug",@"Sep",@"Oct",@"Nov",@"Dec"];
    } else if (/* 其他情况 */) {
        // 返回其他结果
    }
}

@end

获取方法并不访问数据,而且属性也不需要由实例变量来实现,属性所需的获取方法已经实现,不会再为属性自动合成实例变量,编译器不会发出警告。

上一篇 下一篇

猜你喜欢

热点阅读