block

作者: 未来的路就在那 | 来源:发表于2018-03-06 10:17 被阅读17次

    理解Block的使用

    1. Block为什么要用copy
      a、block在创建的时候默认分配的内存是在栈上,而不是在堆上。这样的话其本身的作用域是属于创建时候
      的作用域,一旦在创建的作用域之外调用就会导致程序的崩溃。所以使用了copy将其拷贝到堆内存上。
      b、block创建在栈上,而block的代码中可能会用到本地的一些变量,只有将其拷贝到堆上,才能用这些变量

    2. Block为什么不用retain
      retain这是增加了一次计数,block的内存还是在栈上,并没有存在堆上,存在栈上的block可能随时被系统回收。

    3. 为什么进入block中的对象引用计数需要自动加1
      Block执行的是回调,因此block并不知道其中的对象obj创建后会在什么时候被释放,为了不在block使用obj之前,对象已经被释放,block就retain了obj一次

    4. block和函数的关系
      Block的使用很像函数指针,不过与函数最大的不同是Block可以访问函数以外、词法作用域以内的外部变量的值。
      换句话说,Block不仅 实现函数的功能,还能携带函数的执行环境。

    5. 对于block的理解
      block实际上是: 指向结构体的指针
      编译器会将block的内部代码生成对应的函数

    6. 对于基本数据类型,进入到block中会被当做常量处理

     //如果需要在block中对num进行修改,需要加上关键字__block
     //(我们也可以用static关键字进行修饰)
      int  num1 = 10;
      void(^block1)() = ^{
           NSLog(@"num1 is %d",num1);
      };
      num1 = 20;
      block1(); //输出10
    
      //改进:使用block,使进入到block块中的变量不被当做常量来使用
      __blockint  num2 = 10;
      void(^block2)() = ^{
         NSLog(@"num2 is %d",num2);
      };
      num2 = 20;
      block2(); //输出20
    
    
    1. Block中self的循环引用
      block默认创建在栈上,所以对要对其进行执行copy操作,将其拷贝到堆区,便于更好的操作对象。但是执行了copy操作之后,block中使用self,此对象会被retain一次(注意:block在堆区上时才会起到retain作用),会造成循环引用。
      解决方法:
      在MRC下,使用__block修饰
      在ARC下,使用__unsafe_unretained\weak修饰

    KVO基本原理:

    1.KVO是基于runtime机制实现的

    2.当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的通知机制

    3.如果原类为Person,那么生成的派生类名为NSKVONotifying_Person

    4.每个类对象中都有一个isa指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的setter方法

    5.键值观察通知依赖于NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey:;在一个被观察属性发生改变之前, willChangeValueForKey:一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey:会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。

    KVO深入原理:

    1.Apple 使用了 isa 混写(isa-swizzling)来实现 KVO 。当观察对象A时,KVO机制动态创建一个新的名为: NSKVONotifying_A的新类,该类继承自对象A的本类,且KVO为NSKVONotifying_A重写观察属性的setter 方法,setter 方法会负责在调用原 setter 方法之前和之后,通知所有观察对象属性值的更改情况。

    2.NSKVONotifying_A类剖析:在这个过程,被观察对象的 isa 指针从指向原来的A类,被KVO机制修改为指向系统新创建的子类 NSKVONotifying_A类,来实现当前类属性值改变的监听;

    3.所以当我们从应用层面上看来,完全没有意识到有新的类出现,这是系统“隐瞒”了对KVO的底层实现过程,让我们误以为还是原来的类。但是此时如果我们创建一个新的名为“NSKVONotifying_A”的类(),就会发现系统运行到注册KVO的那段代码时程序就崩溃,因为系统在注册监听的时候动态创建了名为NSKVONotifying_A的中间类,并指向这个中间类了。

    4.(isa 指针的作用:每个对象都有isa 指针,指向该对象的类,它告诉 Runtime 系统这个对象的类是什么。所以对象注册为观察者时,isa指针指向新子类,那么这个被观察的对象就神奇地变成新子类的对象(或实例)了。) 因而在该对象上对 setter 的调用就会调用已重写的 setter,从而激活键值通知机制。

    5.子类setter方法剖析:KVO的键值观察通知依赖于 NSObject 的两个方法:willChangeValueForKey:和 didChangevlueForKey:,在存取数值的前后分别调用2个方法: 被观察属性发生改变之前,willChangeValueForKey:被调用,通知系统该keyPath 的属性值即将变更;当改变发生后, didChangeValueForKey: 被调用,通知系统该 keyPath 的属性值已经变更;之后, observeValueForKey:ofObject:change:context: 也会被调用。且重写观察属性的setter 方法这种继承方式的注入是在运行时而不是编译时实现的。

    相关文章

      网友评论

          本文标题:block

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