zoukankan      html  css  js  c++  java
  • Effective Objective-C 2.0重读笔记---2

    1. 很多时候我们需要保证读写数据的安全性,这时候最好不要使用@synchronized同步块,因为同步块中的代码必须单独执行,这有可能会使当前的代码等许多无关的代码执行完毕才能继续执行,降低程序运行效率。此外还有NSLock ,NSRecursiveLock这些锁,但是这些锁也应该少用,

    最好的办法就是用GCD队列保证数据的安全性,而且GCD基于XUN内核,提供了很多底层优化

    dispatch_queue_t serialQ = dispatch_queue_create("testGCDQueue",DISPATCH_QUEUE_SERIAL);
            dispatch_barrier_async(serialQ, ^{
                // safe
            });
    dispatch_queue_t serialQ = dispatch_queue_create("testGCDQueue",DISPATCH_QUEUE_SERIAL);
            dispatch_barrier_sync(serialQ, ^{
                // safe
            });

    如果块中的代码量比较少,你会发现asyn居然比syn慢,这是因为asyn需要拷贝块,但是当块里面逻辑运算负责的时候据要看具体情况了

    保证数据安全一般是在set和get方法里,所以一般而且数据是允许多读单写的

    如果使用并发队列,可以再set里面使用syn在get里面使用asyn,但是读写有可能会交叉进行,所以这样不是很安全

    GCD里面提供了一种栅栏块,这种块会等之前的任务块全部执行完毕以后再执行,而栅栏块之后的块需要等到栅栏块执行完毕之后才执行,这样便能提高安全性还能并发执行

     dispatch_barrier_async(cerialQ, ^{
                // safe
            });

    2.performSelector系列方法在内存管理方面有所疏漏,他无法确定将要执行的选择自具体是什么,因而ARC编译器也就无法插入适当的内存管理方法

    而且Per还有很大局限性,per所能实现的功能GCD或者operation都能实现

    3.dipatch_group机制可以将队列分组,调用者可以获悉这组任务何时执行完毕

    dispatch_queue_t aDQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_group_t group = dispatch_group_create();
        // Add a task to the group
        dispatch_group_async(group, aDQueue, ^{
            printf("task 1 
    ");
        });
        dispatch_group_async(group, aDQueue, ^{
            printf("task 2 
    ");
        });
        dispatch_group_async(group, aDQueue, ^{
            printf("task 3 
    ");
        });
        printf("wait 1 2 3 
    ");
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
        printf("task 1 2 3 finished 
    ")

     上面的代码就是如何等待一组任务完成,wait函数会阻塞当前线程DISPATCH_TIME_FOREVER是永远等待,可以传你自己的值比如说穿个1000,如果线程执行的时间大于1000,则会返回非0值,,如果小于1000,则会返回0

    但是我们在开发中如果是UI线程是千万不能阻塞的,

    所以GCD提供了另一个函数dispatch_group_notify()这个是不阻塞线程的通知结束,而且还可以指定组里面的任务完成后,执行哪个块,在哪个队列里执行

    还有一种是利用enter函数和leave函数向组里添加任务(这两个一定要成对出现)否则该队列里的任务总是玩成不了

    - (void)test1{
        dispatch_queue_t aDQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_group_t group = dispatch_group_create();
        for(int i = 0; i < 100; i++)
        {
            dispatch_group_enter(group);
            dispatch_async(aDQueue, ^{
                NSLog(@"--%d--",i);
                dispatch_group_leave(group);
            });
        }
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
        NSLog(@"finish");
    }

    enter要放在外面,leave要放在块里最后一句上,这样才能保证正常执行,

    4.用dispatch_once执行只需要执行一次的代码

      static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            
        });

    实现单例时经常会用到上面的代码,这个是@synchronized性能的两倍

    5.另外不要使用dispatch_get_current_queue获取当前队列。因为队列是有层级的,你获取的队列可能包括在另一个队列里面。如果用了同步方法,并且在当前队列的上级队列李阿敏执行block,就产生死锁。P183

    6.多使用枚举块,少用for循环。遍历NSArray,NSDictionary或者NSSet的时候枚举块的效率比for高,尤其是NSDictionary的时候,

    OC1.0的时候使用的NSEnumerator来遍历集合类(NSEnumerator *enumeratoe = [array reverseObjectEnumerator]; 可以执行反向遍历)

     NSArray *array = @[@"1",@"2",@"3"];
        NSEnumerator *enumeratoe = [array objectEnumerator];
        
        id object;
        while (object= [enumeratoe nextObject]) {
            NSLog(@"%@",object);
        }
      NSDictionary *dic = @{@"1":@"yi",@"2":@"er",@"3":@"san"};
      NSEnumerator  *enumeratoe = [dic keyEnumerator];
        id key;
        while (key = [enumeratoe nextObject]) {
            id value = dic[key];
            NSLog(@"%@",value);
        }

    NSSet就不贴了,gen NSArray一样

    OC2.0引入了快速遍历

    就是我们常用的for in 

    for in 也可以执行反向遍历

     for (id obj in [array reverseObjectEnumerator]) {
             NSLog(@"%@",obj);
        }

    基于集合类块的方式也很好用,这种方式尤其是对于字典来说效率很高,因为不需要额外创建一个key的array,而且不用去hash查找,虽然hash效率很高

    7.书上讲如何从CFArray等CoreFoundation框架中的数据结构,自定义数组和字典,自己设置内存管理语义,不过貌似用的不多~

    8.构建缓存的时候最好使用NSCache而非NSDictionary

    NSCache类是专门为缓存设计的,在用作缓存的时候很多方面优于NSDictionary,

    --1.当系统资源快要耗尽的时候NSCache可以自动删除缓存,而且他还会优先删除最长最久未使用的数据

    --2.另外NSCache不会自动拷贝键,而NSDictionary要实现此功能需要复杂的代码(有时候键的类型不支持自动拷贝)

    --3.并且NSCache是线程安全的,而NSDictionary则不具备此优势

    --4.可以给NSCache指定总缓存对象数和总缓存大小

    指定NSCache开销值时应注意,如果加入的对象是NSData等这种可以直接读取出大小的,可以用。其他的最好不要用,因为计算开销值要访问磁盘等操作会耗费资源和时间

    而使用NSCache的目的就是节省资源和时间

    NSCache用法很简单直接去看头文件就可以了,跟NSDictionary很像

    有一个NSData的子类可以根据需要从缓存里直接删除

    NSPurgeableData,当该类对象销毁时会自动从缓存中删除,通过NSCache的evictsObjectsWithDiscardedContent属性可以开启此功能

    NSPurgeableData通过 beginContentAccess方法和endContentAccess方法为自己管理(类似于计数器)调用begin然后调用end变回回收

    9.类加入运行期系统的时候会调用load方法,对于每个类和每个分类来说都会调用此方法调用顺序是类-》分类

    如果要使用load方法,应当十分小心,因为你不知道此时哪些类已经装载好了,哪些类还没有被装载

    此外load方法不遵从继承调用规则,如果子类没有实现load,不管其父类是否实现,都不会调用这个方法

    load方法应该十分精简或者不调用,因为程序执行load的时候会阻塞,其真正的用途不是实现功能而是调试代码*(看看分类是否已经加载)

    跟load相似的方法是initialize方法,应用程序在首次使用某个类的时候会调用这个方法,使用才会调,如果一直不使用就一直不调用

    此时的系统不像load时那么脆弱,initialize是遵从继承体系了,如果子类未实现而父类实现了。便会调用父类的方法,此时可以通过iskindofclass等方法判断是哪个类,进而做一些处理

    10. NStimer会保留期执行函数中所在的对象,注意释放,

  • 相关阅读:
    我爬了《流浪地球》十万个短评得出以下结论
    Activiti开发案例之代码生成工作流图片
    Activiti开发案例之activiti-app工作流导出图片
    用MySQL语法建 一个学生表,包括学生姓名、性别、年龄、班级信息。
    事务是什么,以及事务四个特性
    精选30道Java笔试题解答
    父类和子类的构造方法的调用顺序
    Java的修饰符
    volatile修饰符
    Java中普通代码块,构造代码块,静态代码块区别及代码示例
  • 原文地址:https://www.cnblogs.com/xyzaijing/p/3892513.html
Copyright © 2011-2022 走看看