block与协议-代理

1.block类型-存储代码块的类型

在异步编程时常需要进行函数回调,在C#中会用匿名委托或者lambda表达式讲一个操作作为参数进行传递.
ObjC中是使用对于闭包的实现,在块状中我们可以持有或引用局部变量. 同时利用Block可以将一个操作作为参数进行传递;

blcok用法:

  • 定义:返回值类型 ( ^变量名 ) ( 形参类型 );
  • 赋值:变量名=^(形参){
    代码块+形参变量
    };
  • 使用:变量(实参);

例:

    int (^myBlcok)(int ,int)=^(int m,int n){
      return  m+n;
      }; //无参数时大括号前()可省略

      myBlock(10,5);    //调用块,省略了接受块返回值;

总结:经过简单了解C与OC;发现从最小的一个变量到表达式到一个函数等,其实只有两点作用: 值(返回值) 与 功能(行为,方法,作用).
一行代码,按使用了值 或 功能来解读比较容易理解.

Block做界面逆传值:

- 如果回调方法比较少,1~2,最好不要超过3个,这个时候使用block比较合适

- 如果回调方法太多,会让代码显得臃肿,反而不好维护

-如果回调方法非常多,同时又不用每一个方法都必须实现,这个时候用delegate会比较方便!

block传值的循环引用问题:

定义使用前加上声明.__weak typeof(obj) weakself = self;

typedef block格式

类似函数指针,直接在定义格式之前加 typedef关键字,之后变量名就是类型的别名了.
typedef viod (^别名)(形参);
一般以后需要使用block作为函数方法的参数时,为方便最好用别名.而在作用返回值时,一定需要别名,因为编译器不能识别做此时类型做何种解释.
延伸:经过测试,block在编译时按代码顺序,而运行时按调用顺序(变量作用域)

block访问外部变量 (面试题)
  • block内部可以访问外部局部变量,但是此时是const copy方式,地址不同,相当于值传递,只读的.如果外部定义时加前缀__block时,内部可改变外部局变值.
  • block内部如果创建了和外部同名的变量,会屏蔽外部作用域.此时内部的变量也存在栈区;
    原因:block本质是代码块,ARC下创建的时候在堆区,此时代码只是单纯储存,没有功能;当调用的时候,相当于代码增加到main中,这样代码块中创建的变量就跟正常的一样; (block调用完成内部变量即释放,而堆区的只在释放block时一起释放).
  • 如果是静态变量(static修饰局变,生命周期延长,存储在数据区(同初始化的全局))和全局变量.地址传递.此时block存储在全局区.
  • 常量字符串@”abc”,加__block会引用常量变量(如:a变量,a = @”abc”,内部可以任意修改a 指向的内容)的地址。不加block就是@”abc”本身地址,不可变;

三种类型block

根据block在内存中的位置
"NSGlobaBlock"类似函数,存于代码区--全局block
"NNStackBlock"栈区,函数返回后的Block--栈
"NSMallocBlock"堆block--堆
  1. block内没有使用外部变量或是只使用了全局/静态变量时.存于全局代码区,为全局block;—(ARC和MRC下一致)
  2. 当使用外部变量时
    • MRC下,block代码存于栈区;如果此外部变量A存于区,那么A会被copy到block分配的区;如果A是存于区,那么A在block块内与快外相同.
    • ARC下,block代码存于堆区.如果此外部变量A存于区,那么A会被copy到block分配的区;如果A是存于区,那么A在block块内与快外相同.
  3. 如果需要修改外部变量,需要在变量前面声明__Block;
    当使用下划线Block修饰外部变量时:
    • MRC下,无论变量A存于还是区,A在block块内与快外相同;
    • ARC下,如果此外部变量A存于区,那么A会被转移而不是复制区;如果A是存于区,那么A在block块内与快外相同.

**面试题:**block的@property参数(内存管理参数)为什么要用copy:如果不用copy,此时不论ARC还是MRC都是栈Bolck,栈block会提前释放(}),导致无法继续使用;可以copy到堆区手动管理内存.(而字符串copy是防止字符串如果是非常量的,外部可变,造成非预估的结果;)

block在MRC下得内存隐患(NNStacKBlock)

Block_copy将block及内部变量拷贝到堆区.
使用完毕用Blok_release(block变量)释放此堆区空间;

block使用技巧

  1. block结构快速显示:inlineBlock…(也可右下角自定义快速显示其他格式)
  2. Block作为方法参数时,最好把参数列表部分加上,这样后面调用方法时,会自动有格式;
  3. 做方法参数时,需要加上返回值类型;
  4. 做返回值时,先定义别名,最后别忘记执行返回值;
  5. 方法中,void(^)()表示block类型同int,做参和返回值;做实例变量
    @property (nonatomic, copy) void(^变量名)()
  6. get点语法获取block类型实例变量时,自动执行,后面需加();

2.protocl协议的概念及使用

在ObjC中使用@protocol定义一组方法规范. 实现此协议的类 也必须实现对应的方法. 面向对象的语言接口本身是对象行为描述的 协议规范, 也就是说ObjC中@protocol和其他语言的接口定义是类似的, 只是ObjC的@interface关键字已经用于定义类了, 因此不会像C# 和Java中那样使用interface来定义接口;

1.定义:

只声明而不实现方法 ,遵守此协议的类要实现协议声明的特定方法; .h
(有@class声明类似)
@protocol 协议名 <系统协议>,<其他协议2>…
//方法声明列表
@end

2.类遵守协议:

类声明文件中:

@interface 类名:父类<协议1,协议2>
@end

3.实现协议:

只需在此类的.m文件中实现协议声明方法即可(有些必须实现,有些选择实现);

4.应用:

  1. 协议只声明多个方法,不实现, 不能定义属性;
  2. 只要遵守协议,就可以拥有协议的所有方法声明;
  3. 一个类遵守协议而实现的方法,声明和实现都会遗传给子类,而没有实现的方法声明会遗传给子类,即协议可以 继承 ;
  4. 协议和类可以多对多,而一个协议又可以继承其他一个或多个协议.
  5. 所有协议默认遵守NSObject的基协议,其中声明了很多基本方法.
    • 协议中有两个关键字:@required(默认)必须实现的.
    • @optional 选择实现;作用域类似@public等关键字作用域

两大作用

1. 做类型限制:程序员间交流

格式:
id<协议名>obj;//表明 如果要给obj赋值,则赋值的对象必须遵守"协议"才可以赋值.不满足会有警告;
即id<协议名> obj=[类1 new],必须类1要遵守此协议;id 可换成类名,记得加*;
引申的,在对象做实例变量时也可限制,例:@prperty Dog<协议> *dog

2.代理设计模式
使两个不同意义的事物分离解耦.
一种常见编程思想:有些事自己不好做,找代理做;传入的对象,代替当前类完成摸个功能;
原理:对象关联关系的类型限制(A对象拥有遵守特定协议的对象B)白手套作为主子的实例变量;
应用场合:

  • 当A发生了一些行为(ˇˍˇ) 想~告诉B或B想监听对象A的一些行为
  • A无法处理某些行为的时候,让B帮忙;

思路:

  1. 先定义一个协议.
  2. 定义代理类,遵守此协议.
  3. 主类定义限定此协议类型的属性.
  4. 主类中要有行为 来触发代理.
  5. 设置代理.

    • 用父类还是id<协议>
    • 不使用父类的原因:
    • 如果抽象一个父类的话, 还是有局限性, 因为很多时候, 不同类是无法抽象出共同的父类
      的. , 不能多继承

Category

  1. 作用:在不修改原类的情况下扩展其他功能
  2. 目的:对类方法进行归类,便于分模块开发大型类,团队协作.
    声明:@interface 类名(分类名)
    实现:@implementation 类名
    使用原类直接调用

注意:
1)分类不能扩展任何新的实例变量;
2)分类可以访问原类的成员变量;(相当于原类方法调用)
3)如果分类中存在和原类同名方法,优先使用分类(功能更新);
4)如果多个分类都有同名方法,调用最后一个参与编译的分类同名方法;在Compile Sources 文件编译可以看到顺序;

分类的非正式协议:(面试题)

就是NSObject(和系统类框架)的类别(分类)即系统类的分类,增加了所有类的功能;类别接口中指定的方法可能会或者可能不会被框架类实际地实现,而是被子类重写.

分类的延展Extension (匿名分类)

延展是分类一个特例,匿名.且新添加的方法一样要予以实现

@interface MyClass () {
    float value;    //可以新增实例变量
 }
-(void)setValue:(float)newValue;
@end

使用:

  1. 可以在原类.h直接声明,原类.m中实现;不需要自己的.m文件;
  2. 主要用于新增类的私有方法和私有变量.()

你可能感兴趣的:(objC)