美文网首页
《Effective Objective-C 2.0》之系统框架

《Effective Objective-C 2.0》之系统框架

作者: 奶茶007 | 来源:发表于2017-02-04 10:14 被阅读7次

    学习笔记:
    使用C语言来实现API的
    好处:
    可以绕过Objective-C的运行期系统,从而提升执行效率。
    缺点:
    由于ARC只负责Objective-C的对象,所以使用这些API时尤其需要注意内存管理问题。

    用纯C写成的框架与用Objective-C写成的一样重要,若想成为优秀的Objective-C开发者,应该掌握C语言的核心概念。
    

    多用块枚举,少用for循环

    遍历collection有四种方式。最基本的办法是for循环,其次是NSEnumerator遍历法及快速遍历法,最新,最先进的方式是“块枚举法”。
    若提前知道待遍历的collection含有何种对象,则应修改块签名,指出对象的具体类型。
    

    对自定义其内存管理语义的collection使用无缝桥接
    转换操作中的__bridge告诉ARC如何处理转换所涉及的Objective-C对象。
    __bridge本身的意思是:ARC仍然具备这个Objective-C对象的所有权。

        NSArray *anArray = @[@1,@2];
        CFArrayRef aCFArray = (__bridge CFArrayRef)anArray;
        NSLog(@"Size of array = %li",CFArrayGetCount(aCFArray));
    
    

    而__bridge_retained则与之相反,意味着ARC将交出对象的所有权。若是前面那段代码改用它来实现,那么用完数组之后就要加上CFRelese(aCFArray)以释放其内存。

        NSArray *anArray = @[@1,@2];
        CFArrayRef aCFArray = (__bridge_retained  CFArrayRef)anArray;
        NSLog(@"Size of array = %li",CFArrayGetCount(aCFArray));
        CFRelease(aCFArray);
    

    如果想把CFArrayRef转换为NSArray *,并且想令ARC获得对象的所有权,那么就可以采用此种转换方式。

        NSArray *anArray = @[@1,@2];
        CFArrayRef aCFArray = (__bridge  CFArrayRef)anArray;
        NSLog(@"Size of array = %li",CFArrayGetCount(aCFArray));
        NSArray *anotherArray = (__bridge_transfer NSArray *)aCFArray;
    
    

    这三种转换方式称为“桥式转换”。

    为什么纯Objective-C来编写应用程序时,为什么要用到这种功能。
    因为Foundation框架中的Objective-C类所具备的某些功能,是CoreFoundation框架中的C语言数据结构所不具备的,反之亦然。

    在使用Foundation框架中的字典对象时会遇到一个大问题,那就是其键的内存管理语义为“拷贝”,而值得管理语义为“保留”。除非使用强大的无缝桥接技术,否则无法改变其语义。

    #import "EOCClass.h"
    #import <Foundation/Foundation.h>
    #import <CoreFoundation/CoreFoundation.h>
    //initializer element is not a compile-time constant
    CFMutableDictionaryRef aCFDictionary = nil;
    NSMutableDictionary *anDictionary = nil;
    
    @implementation EOCClass
    const void * EOCRetainCallBack(CFAllocatorRef allocator,const void * value){
        return CFRetain(value);
    }
    
     void EOCReleaseCallBack(CFAllocatorRef allocator,const void * value){
        CFRelease(value);
    }
    
    CFDictionaryKeyCallBacks keyCallbacks = {
        0,
        EOCRetainCallBack,
        EOCReleaseCallBack,
        NULL,
        CFEqual,
        CFHash
    };
    CFDictionaryValueCallBacks valueCallbacks = {
        0,
        EOCRetainCallBack,
        EOCReleaseCallBack,
        NULL,
        CFEqual
    };
    + (void)initialize{
        aCFDictionary = CFDictionaryCreateMutable(NULL, 0, &keyCallbacks, & valueCallbacks);
        anDictionary = (__bridge_transfer NSMutableDictionary*)aCFDictionary;
        [anDictionary setValue:@"a" forKey:self];
       //这里不会报错,po打印出anDictionary是
      //{
      //   EOCClass = a;
     //}
     //此处修改内存管理语义,对键执行“保留”而非“拷贝”操作。
    }
    
    - (instancetype)init{
        if (self = [super init]) {
            self.eocDictionary = anDictionary;
            [self.eocDictionary setValue:@"a" forKey:self];
           //这里直接报错 [EOCClass copyWithZone:]: unrecognized selector sent to       //instance 0x6080000015b0
          //该错误表明,对象所属的类不支持NSCopying协议,因为“copyWithZone:”方法未实现。
        }
        return self;
    }
    
    

    在CoreFoundation层面创建collection时,可以指定许多回调函数,这些函数表示collection应如何处理其元素。然后,可运用无缝桥接技术,将其转换成具备特殊内存管理语义的Objective-C collection。

    精简initialize与load的实现代码
    initialize与load方法要保持精简,原因是:对于某个类来说,任何线程都可能成为初次用到它的线程,如果这个线程恰好是UI线程,那么初始化期间就会一直阻塞,导致应用程序无响应;减少进入“依赖环”的几率。
    load方法:
    对于加入运行期系统的每个类和分类,必定会调用此方法,而且仅调用一次。在iOS平台程序启动时候,就会执行此方法。如果分类和其所属的类都定义了load方法,则先调用类里,再调用分类。需要注意的是:load方法不遵从继承规则。,如果某个类本身没有实现load方法,那么不管其超类是否实现,系统都不会调用。

    缺点:
    一,load方法中使用其他类是不安全的。

    #import "EOCClassB.h"
    #import "EOCClassA.h"
    @implementation EOCClassB
    + (void)load{
        NSLog(@"Loading EOCClassB");
        EOCClassA *eOCClassA = [EOCClassA new];
    }
    @end
    

    调用NSLog没有问题,因为Foundation肯定在运行load之前就已经载入系统了。但是使用EOCClassA却不太安全,不能确定EOCClassA此时是否已经加载好了。
    二,应用程序必须阻塞并等待所有类的load方法执行完,才能继续。
    用途:
    调试程序,比如可以在分类里编写此方法,用来判断该分类是否已经正确载入系统。

    initialize方法
    特点:
    1,惰性调用,只有当程序用到了相关类,才会调用。
    2,运行期系统确保initialize方法一定在线程安全的环境中执行。只有执行initialize哪个线程可以操作类或类实例。其他线程都要先阻塞,等待initialize执行完。
    3,initialize和其他消息一样,如果某个类未实现它,而其超类实现了,就会运行超类实现代码。
    作用:
    只应该来设置内部数据,初始化全局状态,如果还有其他事情要做,可以专门创建一个方法来执行这些操作,并要求该类的使用者必须在使用本类之前调用此方法。

    static const int KInterval = 10;
     NSMutableArray *kSomeArray ;
    @implementation EOCClassA
    + (void)initialize{
        if (self == [EOCClassA class]) {
            [EOCClassB doSomeThingB];
            kSomeArray = [NSMutableArray array];
        }
    
        
    }
    
    + (void)doSomeThingA{
      
    }
    @end
    

    整数在编译器可以定义,然而可变数组不行,因为它是个Objective-C对象,所以创建实例之前必须先激活运行期系统。注意某些Objective-C对象也可以在编译器创建,例如NSString实例。

    相关文章

      网友评论

          本文标题:《Effective Objective-C 2.0》之系统框架

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