第24条:将类的实现代码分散到便于管理的数个分类之中
- 什么是分类?
例如,我们把个人信息建模为类。
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface EOCPerson : NSObject
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
@property (nonatomic, strong, readonly) NSSet *friends;
-(id)initWithFirstName:(NSString *)firstName
lastName:(NSString *)lastName;
/** Friendship methods */
-(void)addFriend:(EOCPerson *)person;
-(void)removeFriend:(EOCPerson *)person;
-(BOOL)isFriendsWith:(EOCPerson *)person;
/** Work methods */
-(void)performDaysWork;
-(void)takeVacationFromWork;
/** Play methods */
-(void)goToTheCinema;
-(void)goToSportsGame;
@end
可以用“分类”机制把刚才的类改写成下面这样:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface EOCPerson : NSObject
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
@property (nonatomic, strong, readonly) NSSet *friends;
-(id)initWithFirstName:(NSString *)firstName
lastName:(NSString *)lastName;
@end
@interface EOCPerson (Friendship)
-(void)addFriend:(EOCPerson *)person;
-(void)removeFriend:(EOCPerson *)person;
-(BOOL)isFriendsWith:(EOCPerson *)person;
@end
@interface EOCPerson (Work)
-(void)performDaysWork;
-(void)takeVacationFromWork;
@end
@interface EOCPerson (Play)
-(void)goToTheCinema;
-(void)goToSportsGame;
@end
在本例中,类的基本要素(属性与初始化方法等)都声明在“主实现(mainimplementation)”里。
分类后,又可拆分成如下文件:
EocPerson+Friendship(.h/.m)
EocPerson+Work(.h/.m)
EocPerson+Play(.h/.m)
-
如何创建分类示意
创建分类1
创建分类2
#import "EOCPerson.h"
NS_ASSUME_NONNULL_BEGIN
@interface EOCPerson (Friendship)
-(void)addFriend:(EOCPerson *)person;
-(void)removeFriend:(EOCPerson *)person;
-(BOOL)isFriendsWith:(EOCPerson *)person;
@end
NS_ASSUME_NONNULL_END
使用分类后,如果想用分类中的方法,要记得在引入EOCPerson.h时一并引入分类的头文件。
-
分类的好处。
3.1 易于管理
3.2 便于调试
image
3.3 隐藏实现细节
在编写准备分享给其他开发者使用的程序库时,可以考虑创建Private分类。如果程序库中的某个地方要用到这些方法,就引入此分类的头文件。而分类的头文件并不随程序库一并公开,于是该库的使用者也就不知道库里还有这些私有方法了。
第25条:总是为第三方类的分类名称加前缀
向第三方类中添加分类时,总应给其名称加上你专用的前缀。
向第三方类中添加分类时,总应给其中的方法名加上你专用的前缀。
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSString (CSK_HTTP)
//Encode a string with URL encoding
-(NSString *)csk_urlEncodedString;
//Decode a URL encoded string
-(NSString *)csk_urlDecodedString;
@end
NS_ASSUME_NONNULL_END
避免莫名其妙的错误,因为如果你覆盖掉了官方的后者别人的代码,或者别人覆盖掉了你的代码,这种bug是很难追查的。
第26条:勿在分类中声明属性
- 把封装所用的全部属性都定义在主接口里。
- 属性所要表达的意思是:类中有数据在支持着它。属性是用来封装数据的。
#import "EOCPerson.h"
NS_ASSUME_NONNULL_BEGIN
@interface EOCPerson (Friendship)
@property (nonatomic, strong) NSArray *friends;
-(void)addFriend:(EOCPerson *)person;
-(void)removeFriend:(EOCPerson *)person;
-(BOOL)isFriendsWith:(EOCPerson *)person;
@end
NS_ASSUME_NONNULL_END
例如,这段代码将属性添加到分类中,编译的时候会出现如下警告信息。
image
意思是说此分类无法合成与friends属性相关的实例变量,所以开发者需要在分类中为该属性实现存取方法。
可以使用关联对象(参见第10条)来解决在分类中不能合成实例变量的问题。
#import "EOCPerson+Friendship.h"
#import <objc/runtime.h>
staticconstchar *kFriendsPropertyKey = "kFriendsPropertyKey";
@implementation EOCPerson (Friendship)
-(NSArray *)friends
{
returnobjc_getAssociatedObject(self, kFriendsPropertyKey);
}
-(void)setFriends:(NSArray *)friends
{
objc_setAssociatedObject(self,
kFriendsPropertyKey,
friends,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
这样做可行,但不太理想。因为我们再为属性实现存取方法的时候,经常会忘记遵从其内存管理语义。不推荐。
网友评论