• NSNull之研究


    为什么会有NSNull?

    Objective-C是C的一个超集,主要引入了OO的设计理念。所以,Objective-C不可避免地使用指针以及指针变量来描述一个对象的内存地址。那么,既然存在指针这种东西,当然就允许存在NULL指针,也就是空指针。

    另外,Objective-C主要定义了两种容器:NSArray和NSDictionary,并且规定了这两种容器中都不能放置nil指针,只能存放NSObject对象。那么就存在有些场景确实需要存nil指针,可能是出于保证count的正确性的目的。所以,在需要存放nil指针的位置就存放了NSNull对象,用来表示空对象,除此之外无任何实际意义。这也就是为什么Cocoa需要设计NSNull的原因。

    NSNull带来的问题

    NSNull虽然可以保证容器count的正确性,但是对于我们的业务来说却是一种麻烦。虽然NSArray和NSDictionary里皆可存放不同类型的对象,通常使用时存放的都为同一类型的对象,这样我们便可以直接使用foreach遍历容器。但是,NSNull给我们带来了问题,我们不确定容器里是否包含NSNull对象,使用每个容器时都需要过滤一下里边包含的NSNull对象,如果不加处理地假定容器中未包含NSNull,则很可能带来crash。

    sqlite3中定义的NULL类型,以及从服务端传来的json数据,都会生成一个对应的NSNull对象。甚至kvo的使用,导致NSNull对象遍布系统中。

    一种替代NSNull的技术

    如果每次使用容器时都做过滤NSNull的操作,可能会带来一下几个问题:

    • 额外的计算量。
    • 冗余的代码。
    • 代码略显奇怪。

    回想一下,最常使用的也就两种数据:数字和字符串,对应Objective-C中的NSNumber和NSString。并且Objective-C作为一种动态绑定的语言,也就是我们常说的Runtime Binding。在Objective-C中每一个method都包含一个implementation和selector,Runtime根据每个对象接收的selector通过Objective-C类对象模型找到对应的implementation。Objective-C的Runtime提供一种Forwarding技术,可以将该对象无法处理的消息转发给某个可以响应这个消息的对象,如果最终无法找到一个可以响应该消息的对象,那么将抛出一个NSInvalidArgumentException。利用这种机制,我们便可根据接收的消息返回一个可以响应该消息的默认对象。比如,接收charValue消息便可返回一个@0对象,而接收intValue消息即可返回一个@“”对象。code 如下:

    - (id)forwardingTargetForSelector:(SEL)aSelector {
        if ([NSString instancesRespondToSelector:aSelector]) {
          return @"";
        }else if ([NSNumber instancesRespondToSelector:aSelector]) {
           return @0;    
         }else {
        return nil;
         }
    }
    

    根据这种技术,设计了GNGeneralNullValue类,它可以自动地替换系统定义的NSNull,所有发往NSNull类生产的对象的消息都将发送给GNGeneralNullValue类所生产的对象,然后根据接收的消息返回一个可以响应的具有默认值的对象。同时,GNGeneralNullValue还支持添加用户自定义的类,除了基本的数据类型。根据所写的单元测试代码,可以一窥其使用方式。

    - (void)test_NSNumber_charValue_shouldResponse {
        NSNumber *number = (NSNumber *)[GNGeneralNullValue 
        generalNullValue];   
        XCTAssertEqual(0, [number charValue]);
    }
    
    - (void)test_NSNumber_floatValue_shouldResponse {
        NSNumber *number = (NSNumber *)[GNGeneralNullValue 
        generalNullValue];
        XCTAssertEqual(0.0f, [number floatValue]);
    }
    

    其具体的实现和源码地址为GNGeneralNullValue,欢迎反馈问题和交流。其使用非常方便方便,只需包含源码,无需其他操作即可享受远离NSNull的生活。

  • 相关阅读:
    为何url地址不是直接发送到服务器,而是被编码后再发送
    http请求分析
    Nginx+Php不支持并发,导致curl请求卡死(Window环境)
    Vue开发调试神器 vue-devtools
    什么是闭包?闭包的优缺点?
    Nginx 504 Gateway Time-out分析及解决方法
    HTTP请求8种方法
    MySQL查询缓存总结
    MySQL单表多次查询和多表联合查询,哪个效率高?
    分布式系统一致性问题解决实战
  • 原文地址:https://www.cnblogs.com/CoderPlace/p/4411589.html
Copyright © 2020-2023  润新知