封装YYCategory的实践2018
背景简介
根据YY
作者的自述文章最近一段时间的工作整理整个YYKit
是由一系列文章组成。这个导入YYKit
整体感觉有点乱,最后还是根据需要,分模块导入,分别封装一下后,自己再重新组织一下,换个名字,比如KJTKit
之类的。经过几天的封装,已经差不多了,还剩下YYCategory
,这是小工具,有些很有用,并且类别的使用方式是Object-C
特有的,也很普遍。剩下几个工程中暂时还没有涉及,等以后用到了再说。
![](https://img.haomeiwen.com/i1186939/b96f63d85e47853b.png)
封装NSDate
网络来的日期,一般是毫秒数,通过这个创建NSDate
对象,有现成API
,只是不要忘记将毫秒转化成秒,要除以1000
+ (instancetype)dateWithTimeIntervalSince1970:(NSTimeInterval)secs;
-
NSDate
要显示给用户看到,需要转化为NSString
,这两者的互转用得比较多,需要借助NSDateFormatter
实现,这里直接提供方便工具,比较实用。
// 字符串转日期
+ (nullable NSDate *)kjtDateWithString:(NSString *)dateString format:(NSString *)format {
return [NSDate dateWithString:dateString format:format];
}
// 日期转字符串
- (nullable NSString *)kjtStringWithFormat:(NSString *)format {
return [self stringWithFormat:format];
}
- 临近几天用的比较多,至于临近的月数,年数之类的,等以后用到再封装。
// 相邻几天,昨天就给-1,明天给1
- (nullable NSDate *)kjtDateByAddingDays:(NSInteger)days {
return [self kjtDateByAddingDays:days];
}
封装NSObject
- 提供了KVO的封装,这个比较赞,系统方法实在难用。引入 KVOController又有点小题大做的感觉。
// 添加观察者
- (void)kjtAddBlockForKeyPath:(NSString*)keyPath
block:(void (^)(id _Nonnull obj, id _Nonnull oldVal, id _Nonnull newVal))block {
[self addObserverBlockForKeyPath:keyPath block:block];
}
// 移除所有观察者,防止内存泄漏
- (void)kjtRemoveAllBlocks {
[self removeObserverBlocks];
}
- (void)removeObserverBlocksForKeyPath:(NSString*)keyPath;
是故意不提供的,防止内存泄漏
- 方法交换,涉及
runtime
相关内容,自己写容易出错,用这个比较方便。
// 交换实例方法
+ (BOOL)kjtSwizzleInstanceMethod:(SEL)originalSel with:(SEL)newSel {
return [[self class] swizzleInstanceMethod:originalSel with:newSel];
}
// 交换静态类方法
+ (BOOL)kjtSwizzleClassMethod:(SEL)originalSel with:(SEL)newSel {
return [[self class] swizzleClassMethod:originalSel with:newSel];
}
- 关联变量,涉及
runtime
相关内容,自己写容易出错,用这个比较方便。
// 设置指针型关联变量
- (void)kjtSetAssociateValue:(nullable id)value withKey:(void *)key {
[self setAssociateValue:value withKey:key];
}
// 设置数值型关联变量
- (void)kjtSetAssociateWeakValue:(nullable id)value withKey:(void *)key {
[self setAssociateWeakValue:value withKey:key];
}
// 读取关联变量
- (nullable id)kjtGetAssociatedValueForKey:(void *)key {
return [self getAssociatedValueForKey:key];
}
// 移除所有的关联变量,防止内存泄漏
- (void)kjtRemoveAssociatedValues {
[self removeAssociatedValues];
}
封装NSString
- md5是经常用到的
// md5
- (nullable NSString *)kjtMd5String {
return [self md5String];
}
- base64编码
// base64
- (nullable NSString *)kjtBase64EncodedString {
return [self base64EncodedString];
}
- url编解码,这个要自己写的话很烦人,有很多需要注意的地方
// url编码
- (NSString *)kjtUrlEncodeString {
return [self stringByURLEncode];
}
// url解码
- (NSString *)kjtUrlDecodeString {
return [self stringByURLDecode];
}
- html关键字处理,就是加&
// 标签关键字替换为&字符
- (NSString *)kjtEscapingHTMLString {
return [self stringByEscapingHTML];
}
- 去掉空格,在用户录入的时候经常用到
// 去掉首尾空格
- (NSString *)kjtTrimString {
return [self stringByTrim];
}
- 字符串判空,nil是空,@“ ”也是空
// 判空
- (BOOL)kjtIsEmpty {
return ![self isNotBlank];
}
- 字符集判断包含;子串包含iOS8已经提供,这里不重复封装
// 判断是否包含字符集; - (BOOL)containsString:(NSString *)string; iOS8已经提供,这里不重复
- (BOOL)kjtContainsCharacterSet:(NSCharacterSet *)set {
return [self containsCharacterSet:set];
}
- json解码
// json解码
- (id)kjtDecodeJson {
return [self jsonValueDecoded];
}
- 读文件内容
// 从文件读内容
+ (NSString *)kjtFileStringNamed:(NSString *)name {
return [self stringNamed:name];
}
封装UIApplication
属性类型的接口并不是很友好,全部改成函数形式的。
另外,实例方法要通过[UIApplication sharedApplication]
访问,不是很方便,所以都改成类的静态方法。
- document和cache的路径,包括
NSURL, NSString
两种格式
// 文档和cache的路径
+ (NSURL *)kjtDocumentsURL {
return [UIApplication sharedApplication].documentsURL;
}
+ (NSString *)kjtDocumentsPath {
return [UIApplication sharedApplication].documentsPath;
}
+ (NSURL *)kjtCachesURL {
return [UIApplication sharedApplication].cachesURL;
}
+ (NSString *)kjtCachesPath {
return [UIApplication sharedApplication].cachesPath;
}
- 应用标识符
// 应用标识符
+ (NSString *)kjtAppBundleName {
return [UIApplication sharedApplication].appBundleName;
}
+ (NSString *)kjtAppBundleID {
return [UIApplication sharedApplication].appBundleID;
}
- 应用版本号,系统接口要记忆字典的键值,并且命名很不友好,取个好名字,用起来就方便多了。
// 版本号
+ (NSString *)kjtAppVersion {
return [UIApplication sharedApplication].appVersion;
}
+ (NSString *)kjtAppBuildVersion {
return [UIApplication sharedApplication].appBuildVersion;
}
- 检查是否盗版,是否从苹果市场安装。是的话,一般是用户的生产版本。要判断签名,实现起来还是蛮麻烦的。
// 是否盗版,这里改了下名字,是否从苹果市场安装
+ (BOOL)kjtIsFromAppstore {
return ![UIApplication sharedApplication].isPirated;
}
NSData封装
这个和NSString
有相似之处,比如加解密这一块。不过又有很大不同,NSData
是机器码,01
类型的数据流,而NSString
是可以写进文档,打印出来看的可见字符。这两者的差异还是很大的,平时使用的时候要注意一下。NSData
不可打印,打印出来,就成了description
,是01
组成的字符了。
- md5,这个太流行了,给个方便方法
// md5
- (NSString *)kjtMd5String {
return [self md5String];
}
- (NSData *)kjtMd5Data {
return [self md5Data];
}
- 转字符串
// 转字符串
- (nullable NSString *)kjtUtf8String {
return [self utf8String];
}
- 与16进制形式字符串互转
// 与16进制字符串互转
- (nullable NSString *)kjtHexString {
return [self hexString];
}
+ (nullable NSData *)kjtDataWithHexString:(NSString *)hexString {
return [NSData dataWithHexString:hexString];
}
- 与base64 字符串互转
// base64 字符串糊状
- (nullable NSString *)kjtBase64EncodedString {
return [self base64EncodedString];
}
+ (nullable NSData *)kjtDataWithBase64EncodedString:(NSString *)base64EncodedString {
return [NSData dataWithBase64EncodedString:base64EncodedString];
}
- json 解码
// json 解码
- (id)kjtDecodeJson {
return [self jsonValueDecoded];
}
- 从文件中读
// 从文件中读
+ (nullable NSData *)kjtFileDataNamed:(NSString *)name {
return [NSData dataNamed:name];
}
封装UIColor
主要是和16进制字符串的互转
// 与16进制字符串互转
+ (nullable UIColor *)kjtColorWithHexString:(NSString *)hexStr {
return [UIColor colorWithHexString:hexStr];
}
- (nullable NSString *)kjtHexString {
return [self hexString];
}
- (nullable NSString *)kjtHexStringWithAlpha {
return [self hexStringWithAlpha];
}
封装UIDevice
这些主要是硬件信息,是属性形式的接口,访问需要[UIDevice currentDevice]
,不是很方便,改成类静态接口。
- 系统版本号
// 系统版本号
+ (double)kjtSystemVersion {
return [UIDevice systemVersion];
}
- 硬件信息
+ (BOOL)kjtIsPad {
return [UIDevice currentDevice].isPad;
}
+ (BOOL)kjtIsSimulator {
return [UIDevice currentDevice].isSimulator;
}
+ (BOOL)kjtIsJailbroken {
return [UIDevice currentDevice].isJailbroken;
}
+ (BOOL)kjtCanMakePhoneCalls {
return [UIDevice currentDevice].canMakePhoneCalls;
}
+ (NSString *)kjtMachineModel {
return [UIDevice currentDevice].machineModel;
}
+ (NSString *)kjtMachineModelName {
return [UIDevice currentDevice].machineModelName;
}
- IP地址,有两个,手机的和WiFi的,目前项目中用WiFi IP地址
// ip 地址
+ (NSString *)kjtIpAddressWIFI {
return [UIDevice currentDevice].ipAddressWIFI;
}
+ (NSString *)kjtIpAddressCell {
return [UIDevice currentDevice].ipAddressCell;
}
- 唯一标识符,现在流行用这个。详情可以参考下面这篇文章:
UUID 浅析
// 唯一标识符
+ (NSString *)kjtIdfvUuidString {
return [[UIDevice currentDevice].identifierForVendor UUIDString];
}
封装NSNotificationCenter
-
这是一种订阅模式,有一个中心,应为有一个中心
NSNotificationCenter
-
NSNotification
是一个对象,包括name,object,userInfo
三部分。这个从系统定义可以看出来
@interface NSNotification : NSObject <NSCopying, NSCoding>
@property (readonly, copy) NSNotificationName name;
@property (nullable, readonly, retain) id object;
@property (nullable, readonly, copy) NSDictionary *userInfo;
- (instancetype)initWithName:(NSNotificationName)name object:(nullable id)object userInfo:(nullable NSDictionary *)userInfo API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)) NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
@end
-
NSNotificationName
可以当做普通的NSString
来理解
typedef NSString *NSNotificationName NS_EXTENSIBLE_STRING_ENUM;
-
一般使用的时候,不会涉及
NSNotification
对象,使用NSNotificationCenter
的单例,比如
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"test" object:nil];
- 添加观察者一般在主程序,问题也不大。但是发送消息,有可能不在主线程,导致消息丢失。所以有必要封装一个函数,保证发送消息在主线程。
+ (void)kjtPostNotificationOnMainThreadWithName:(NSString *)name
object:(nullable id)object
userInfo:(nullable NSDictionary *)userInfo {
[[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:name object:object userInfo:userInfo];
}