一、延展
延展: Extension
1). 是一个特殊的分类,所以延展也是类的一部分
2). 特殊之处:
a. 延展这个特殊的分类没有名字
b. 只有声明没有实现,和本类共享一个实现-
延展的语法
语法:
@interface 本类名 ()
@end没有实现,和本类共享一个实现.
为类添加延展步骤
New File->FileType选择Extension
只有一个.h文件,文件名:本类名_取的文件名.h
这个文明中只有声明
@interface Person ()
@end延展的基本使用
1). 延展的本质是一个类,作为本类的一部分
只不过是一个特殊的分类,没有名字
2). 延展只有声明,没有单独的实现,和本类共享一个实现延展和分类的区别
1). 分类有名字,延展没有名字,是一个匿名的分类
2). 每一个分类都有单独的声明和实现,而延展只有声明,没有单独的实现,和本类共享一个实现
3). 分类中只能新增方法,而延展中任意的成语都可以写
4). 分类中可以写@property但是只会生成getter/setter声明,延展中写@property会自动生成私有属性,也会生成getter/setter的声明和实现延展的应用场景
1). 要为类写一个私有的@property
生成的getter/setter只能在类的内部访问,不能在外部访问
其实,只让@property生成私有属性、生成getter/setter方法的实现.
2). 延展100%的情况下不会独占一个文件,都是将延展直接写在本类的实现文件当中,这个时候,写在延展中的成员,就相当于这个类的私有成员,外部无法访问,只能在本类的实现中访问
3). 当我们想为类定义私有成员的时候,就可以使用延展,将延展定义在这个类的实现当中.虽然可以定义在@implementation中,但是不要这么写,这样不规范,写一个延展,定义在延展当中。
*. 如果要为类写一个私有方法,建议将声明写在延展当中,实现写在本类的实现中
*. 如果想要为类写一个私有的@property,就直接写在延展中
4). 延展天生就是来私有化类的成员
如果类的成员只希望在类的内部访问,那么就将其定义在严重中,如果类的成员允许被外界访问,定义在本类的@interface中
二、block
OC在C的基础上新增了一些数据类型
BOOL/Boolean/class/nil/SEL/id/blockblock是一个数据类型
既然是一个数据类型,那么就可以声明这个数据类型的变量
1). block是一个数据类型,所以我们可以声明一个block类型的变量
2). block类型的变量中专门存储一段代码,可以有参数,可以有返回值.-
block变量的声明
1). 虽然block变量中是用来存储一段代码的,但是一个block变量中并不是任意的一段代码都可以存进去的,而是有限定的。在声明block变量的时候,必须要指定这个block变量存储的代码是否有参数,是否有返回值,一旦指定以后,这个block变量中就只能存储这样的代码了.声明了一个block变量,返回值是void,参数是一个int类型的,这个block变量中就只能存储无返回值,并且有一个int参数的代码段.
2). 声明block变量的语法格式:
返回值类型 (^block变量的名称)(参数列表);
void (^myBlock1)(); 表示声明了一个block类型的变量,叫做myBlock1,这个变量中只能存储没有返回值没有参数的代码段.
3). 最重要
声明block变量的时候要指定这个block变量可以存储的代码段的返回值和参数描述,一旦指定,这个block变量中就只能存储这样的代码了,其他格式的代码段无法存储 -
初始化block变量
1). 原理: 写一个符合block要求的代码段,存储到block变量中就可以了
2). 代码段的书写格式:
^返回值类型(参数列表){
代码段;
};
3). 写一个无参数无返回值的代码段:
这个时候我们将这段代码使用赋值符号存储到无返回值无参数要求的block变量中.void (^myBlock1)(); ^void(){ NSLog(@"我"); NSLog(@"我"); }; 等价于 void (^myBlock1)(); myBlock1 = ^void(){ NSLog(@"我"); NSLog(@"我"); }; 还等价于 void (^myBlock1)() = ^void(){ NSLog(@"我"); NSLog(@"我"); };
4). 有返回值的代码段.
我们可以将这段代码赋值给符合要求的block变量.int (^myBlock2)(); myBlock2 = ^int(){ int num1 = 10 + 20; return num1; };
5). 既有参数又有返回值的代码段.
int (^myBlock3)(int num1, int num2); myBlock3 = ^int(int num1, int num2){ int num3 = num1 + num2; return num3; };
6). 注意
赋值给block变量的代码段必须要符合block变量的要求,否则就会报错. 如何执行存储在block变量中的代码
语法格式: block变量名();
有参数就传参数,有返回值就接.-
关于block简写
1). 如果我们写的代码段没有返回值,那么代码段的void可以省略void (^myBlock1)() = ^(){ NSLog(@"我"); NSLog(@"我"); };
注意: 代码段的返回值如果是void可以省略,声明block变量的返回值不能省略.
2). 如果我们写的代码段没有参数,那么代码段的小括弧可以省略int (^myBlock2)(); myBlock2 = ^int{ int num1 = 10 + 20; return num1; };
强调: 指的是代码段
所以,当一个代码段既没有参数也没有返回值的时候,就只写^void (^myBlock1)() = ^ { NSLog(@"我"); NSLog(@"我"); };
3). 声明block变量的时候,如果有指定的参数,可以只写参数的类型而不写参数名称
int (^myBlock3)(int, int); myBlock3 = ^int(int num1, int num2){ int num3 = num1 + num2; return num3; };
注意: 声明block的时候可以省略,在写代码段的时候类型和名城管都要写
4). 无论代码段是否有返回值,在写代码的时候都可以省略int (^myBlock3)(int, int); myBlock3 = ^(int num1, int num2){ int num3 = num1 + num2; return num3; };
如果代码段中没有返回任何数据,那么它会认为代码段是没有返回值的,如果代码中有返回数据,返回的数据什么类型,它就会认为这个代码段是什么类型.
建议: 不要简写,仍然按照最标准的写法来写block变量和block代码段,因为这样可以提高代码段的阅读性.
typedef简化block
1). 定义block类型长的问题
2). typedef的使用场景:讲一个长类型定义为短类型
3). 使用typedef将长的block类型定义为一个短类型
typedef 返回值类型 (^新类型)(参数列表)
typedef void (^NewType)();
代表重新定义了一个类型叫做NewType,是一个block类型,无参数无返回值的block类型.关于block块访问外部变量的问题
1). 在block代码块的内部可以取定义在外部的变量的值,定义在外部的局部变量和全局变量
2). 可以修改全局变量的值,不能修改局部变量的值.
3). 如果你希望我们定义的局部变量可以允许在block代码块的内部去修改,那么就为这个局部变量加1个__block的修饰符.block是一个数据类型,作为函数参数
1). 如何为函数定义block类型的参数
a. 在小括弧中声明一个指定格式的block变量
b. 调用的时候可以直接将符合要求的代码段传递过去
c. 通过Xcode提示可以快速的生产block代码段
2). block作为参数可以实现的效果
可以将调用者自己写的代码传递到函数的内部执行
3). block作为函数的返回值返回
当将block作为函数的返回值的时候,返回值的类型就必须要使用typedef定义的短语句block与函数
相同点:都是封装一段代码
不同点:
1). block是一个数据类型,函数是一个函数
2). 我们可以声明block类型的变量,函数就只是函数
3). block可以作为函数的参数.
三、协议
协议: protocol.
作用: 专门用来声明一大堆方法(不能声明属性,也不能实现方法,只能用来写方法的声明).
只要某个类遵守了这个协议,就相当于拥有这个协议中的所有的方法声明.-
协议的声明
@protocol 协议名称 <NSObject>
方法的声明;
@end新建一个协议的方式, NewFile->OC-File-protocol
协议的文件名:.h 并且只有一个.h文件在协议中,只能用来声明方法
作用: 专门用来写方法声明的 -
类遵守协议
如果想让一个类拥有协议中定义的所有的方法声明,那么就让这个类遵守这个协议,类只要遵守这个协议,那么这个类就拥有了这个协议中定义的所有的方法的声明了.
@interface 类名 : 父类名 <协议名称>@end
:表示继承
<> 表示遵守的协议当一个类,遵守了一个协议,那么就相当于这个类拥有了协议中定义的所有的方法的声明,这个类只是拥有了这个协议中的声明而已,没有实现,所以,这个类,就应该实现协议中的方法.
如果类不实现协议中的方法,也不会报错,只是编译器会报警告,但是当创建对象,来调用 这个没有的实现的协议中的方法的时候就会报错.
-
类是单继承,但是协议可以多遵守
一个类值能有一个父类,但是一个类可以遵守多个协议
@interface 类名 : 父类名 <协议名称1, 协议名称2...>@end
当一个类遵守了多个协议之后,就相当于这个类拥有了所有协议中定义的方法的声明,那么这个类,就应该实现所有协议中的方法 -
@required 与 @optional
在协议当中,如果方法的声明被@required修饰,那么这个协议的类就必须要实现这个方法,否则编译器会发出警告.
在协议当中,如果方法的声明被@optional修饰,那么遵守这个协议的类可以实现这个方法,也可以不实现这个方法,不实现编译器也不会报警告.无论@required与@optional都可以不实现,编译器不会报错,仍然可以编译运行,唯一的区别:当遵守协议的类不实现协议中的方法的时候,@required会给出一个警告,@optional警告都没有,默认是@required.
-
协议可以从另外一个类继承,也可以多继承.
协议可以继承另外一个协议,A协议继承B协议,那么A协议中不仅有自己的方法的声明,还有B协议中的方法的声明。如果有一个类遵守了A协议,那么这个类就拥有了A、B协议中所有方法的声明.协议之间继承的语法格式
@protocol A协议名称 <B协议名称>@end
代表A协议继承自B协议,A协议中既有自己方法的声明,也有B协议中的方法声明。要求所有的协议直接或者间接的从NSObject基协议继承. -
@protocol类型限制
1). 这个指针指向遵守了指定协议的任意对象,否则就会报警告
NSObject<协议名称> *指针名;
id<协议名称> *指针名;
2). 声明一个指针变量,要求这个指针变量指向的对象必须遵守多个协议
NSObject<协议名称1, 协议名称2> *指针名;
id<协议名称1, 协议名称2> *指针名;3). 定义一个指针,指向遵守了学习协议的学生对象.
Student<StudyProtocol> *stu = [Student new];
4). 为什么
a. 遵守了某个协议的类,就相当于这个类拥有了这个协议所定义的行为
b. 因为要调用对象中的协议方法,只有遵守了这个协议,才能有协议中的方法.