美文网首页
杂七杂八的小tips(持续更新)

杂七杂八的小tips(持续更新)

作者: 我是繁星 | 来源:发表于2019-06-19 22:55 被阅读0次

    一、关于NSNull

    先说说nil和NULL代表的是内存中的一块空地址,NSNull表示的是一个对象,指向一个nil地址。
    文档上这样描述,NSNull一个单例,NSNull是一个用来表示不允许空值集合对象中的空值的。
    其实NSNull存在的意义在于,OC中集合类型只能存储对象不能存储nil。存储nil会报NSInvalidArgumentException。可以用NSNull替代。

    二、self和super

    在方法调用方面self表示调用自己的方法,super表示调用父类方法。
    self其实是类或者对象,super是预编译指令。
    super调用的方法会转换成如下指令:

    //以class方法为例
    [super class]
    //runtime层
    struct objc_super1 {
            __unsafe_unretained id receiver;
            Class superClass;
     };
    struct objc_super1 obj_super = {self,class_getSuperclass(object_getClass(self))};
    id obj3 = objc_msgSendSuper(&obj_super,@selector(class));
    

    可以看出super调用方法的时候会传入当前对象

    三、保证属性读写线程安全的方案

    • 方案一:读写时加锁可以实现需求,但这样所有的属性读写都是原子的,我们的需求是针对同一属性线程安全,所以这样做效率较低。
    • 方案二:可以创建一个串行队列,把读写操作放到这个串行队中同步执行,串行队列的性质可保证线程安全。优化的话,可以将写操作改为异步执行,读操作改为同步执行。这样做的好处在于写操作速度加快,不会阻塞主线程,但是究其根本所有的读写任务还是同步的,所以读写效率并不高。代码如下:
    - (void)setSomeString:(NSString *)someString{
        dispatch_async(self.serialQueue, ^{
            _someString = someString;
        });
    }
    - (NSString *)someString{
        __block NSString * localString;
        dispatch_sync(self.serialQueue, ^{
            localString = _someString;
        })
        return localString;
    }
    
    • 方案三:前面提到,读取操作我们可以并行去做,因为每个属性互不干扰,只要不与读取操作同事执行就可以,栅栏方法dispatch_barrier_async会在队列中必须单独执行,一定不会与其他任务并发,利用这个特性,我可以更好的实现需求。代码如下:
    - (void)setSomeString:(NSString *)someString{
        dispatch_barrier_async(self.syncQueue, ^{
            _someString = someString;
        })
    }
    - (NSString *)someString{
        __block NSString * localString;
        dispatch_sync(self.syncQueue, ^{
            localString = _someString;
        })
        return localString;
    }
    

    以上代码在不同线程读取属性的时候,可以并发读取。同时又保证了读取时的同步。

    • 扩展,我们思考一下为什么不建议属性用atomic关键字,大家都知道,其实atomic就是通过自旋锁来保证读写线程安全的,这样同一对象所有的属性读取都是同步的,而且会阻塞主线程,所以如果必须保证属性的线程安全的话,推荐方案三。那么引申出来,如何保证两类操作之间的线程安全呢?方案三是最好的选择。

    四、GCD与NSOperation

    GCD是纯C的API,而NSOperation是面向对象的,GCD是用块这种轻量级的数据结构来表示任务的,而GCD则是用更加重量级的对象来表示的,NSOperation是基于GCD的那NSOperation的好处如下。

    • NSOperation可以通过cancel方法轻易的取消队列中的任务,而GCD把块安排到队列中后是无法取消的。
    • GCD可以轻易的设置依赖关系,比如一个网络操作需要之前的5个任务处理完毕之后才能进行,我们可以将请求任务依赖于那五个任务,这样简单的可以实现需求,用GCD我们要用dispatch_group或者信号量的同步方式。
    • 通过键值观察可以了解到NSOperation的声明周期,比如属性isCancel、isFinished等等。NSOperation适合进行比GCD更加精密的操作。
    • NSOperation可以执行操作的优先级,队列中优先级高的任务先执行,GCD则没有直接操作优先级的功能,只能操作队列的优先级。
    • NSOperation对象内置了很多方便的子类,比如NSBlockOperation。

    五、用NSCache的几点好处

    • 低内存时自动删减缓存,LRU(最近最少使用)缓存淘汰。
    • 不同于NSDictionary,NSCache并不会拷贝键,而是会保留键,一些不支持拷贝的类也可以作为键。
    • 可以设置最大开销,和数量,但这并不是对cache的硬限制,只做参考。
    • NSCache可以配合NSPurgeableData使用,当NSPurgeableData对象被系统释放的时候,缓存自动清除。

    六、bounds和frame的区别?

    • bounds:相对自身来说的,控件的内部尺寸,如果修改了Bounds,子控件的相对位置也会发生改变。
    • Frame:相对于父控件来说的,是控件的外部尺寸。
      举个例子:
            let view = UIView(frame: CGRect.init(x: 100, y: 100, width: 100, height: 100));
            view.backgroundColor = UIColor.red;
            self.view.addSubview(view);
            
            let subView = UIView(frame: CGRect.init(x: 20, y: 20, width: 60, height: 60));
            subView.backgroundColor = UIColor.blue;
            view.addSubview(subView);
            
            print("viewFrame:\(view.frame) viewBounds:\(view.bounds)  subViewFrame:\(subView.frame)");
            
            view.transform = CGAffineTransform.init(scaleX: 2, y: 2);
            
            print("viewFrame:\(view.frame) viewBounds:\(view.bounds)  subViewFrame:\(subView.frame)");
    
    //打印结果:
    viewFrame:(100.0, 100.0, 100.0, 100.0) viewBounds:(0.0, 0.0, 100.0, 100.0)  subViewFrame:(20.0, 20.0, 60.0, 60.0)
    viewFrame:(50.0, 50.0, 200.0, 200.0) viewBounds:(0.0, 0.0, 100.0, 100.0)  subViewFrame:(20.0, 20.0, 60.0, 60.0)
    

    如上代码给一个视图放大而被,发现其frame改变了,因为锚点默认是中心,所以按中心放大,但是其bounds没有改变,subView的frame也没有改变。

    相关文章

      网友评论

          本文标题:杂七杂八的小tips(持续更新)

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