美文网首页高能
封装YYCategory的实践2018

封装YYCategory的实践2018

作者: 勇往直前888 | 来源:发表于2018-07-31 17:15 被阅读211次

    背景简介

    根据YY作者的自述文章最近一段时间的工作整理整个YYKit是由一系列文章组成。这个导入YYKit整体感觉有点乱,最后还是根据需要,分模块导入,分别封装一下后,自己再重新组织一下,换个名字,比如KJTKit之类的。经过几天的封装,已经差不多了,还剩下YYCategory,这是小工具,有些很有用,并且类别的使用方式是Object-C特有的,也很普遍。剩下几个工程中暂时还没有涉及,等以后用到了再说。

    image.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];
    }
    

    参考文章

    YYCategories

    文档地址

    相关文章

      网友评论

        本文标题:封装YYCategory的实践2018

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