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

一、关于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(持续更新))