非正式协议–> 实际上还是分类.
- 分类: 我们刚才学习分类,都是为自己的类写分类.
: 思考: 能不能为系统自带的类写分类呢?
- 可以的.
- 为系统自带的类写分类.就叫做非正式协议.
- 什么时候你才要去为系统自带的类写1个分类?
系统的那个类,写的挺好.但是如果能够再多加1个方法就完美了.这个时候,就可以使用分类还给它加1个方法就好了.
延展: Extension
一句话解释: 是1个特殊的分类.所以,延展与分类一样是类的一部分.
特殊的地方
- 延展是1个匿名的分类,没有名字.
- 延展只有声明没有实现.和本类共享1个实现
延展的声明.
@interface 本类名 () @end
- 延展没有单独的实现.和本类共享1个实现
- 将方法声明写在延展中 将方法的实现写在本类的实现中.
- 如何添加1个延展.
- 延展虽然是匿名的没有名字,但是还是有文件名
- 延展只有1个.h文件. 没有.m文件. 因为延展没有单独的实现.和本类共享1个实现.
- 延展的使用注意.
- 分类中只能新增方法.可以写@property但是只会生成getter setter的声明.
- 延展中可以写属性.
- 也可以写@property. 会自动的生成私有属性,getter setter的声明. getteer setter的实现.方法声明肯定也是可以的.
- 延展的作用: 肯定不是来把1个类分为多个模块的.因为实现和本类共享1个实现.
也肯定不是来扩展1个类的.因为实现和本类共享1个实现.你新增1个方法 方法的实现还要写在本类的实现中.
1). 思考
- 要为1个类写1个私有的@property.
- 什么时候私有的@property?
- 生成的getter setter方法要是私有的.
延展.
//100%的情况下,延展不会独占1个头文件.
//延展都是写在类的.m文件中.写在implementation上面
@imterface HMStudent ()
{
ing _age;
}
@property NSString* name;
- (void) study;
@end
//下面是.m的实现
@implementation
- (void)study
{
NSLog(@"我叫%@,今年%d岁",self.name,self->_age);
}
@end
//写在延展中的成员,都是这个类的私有成员,只能在这个类的内部访问.无法在外部访问.
- 延展天生就是来:私有化类的成员的.
如果类中有成员需要被私有化.
属性需要被私有化: a. 在本类的@interface中@private b. 在本类的@implementation中. c. 写在延展中.
==必须将其写在延展中.==
方法需要被私有化: 只写实现不写声明. 建议: 私有方法仍然要写声明和实现,只不过声明写在延展中.
@property需要被私有化
- 把
@property
写在延展中.
- 把
如何使用.
如果类中有私有成员, 将这些私有成员全部的写在延展中. 私有属性, 私有方法的声明. 私有@property. 写在延展中的成员,只能在类的内部访问.不能在外部访问
block 是一个数据类型
数据类型的作用
可以声明这个这个数据类型的变量,来存储数据.
int num;
double num;
block类型的变量的声明
block也是1个数据类型.
- 所以我们也可以声明1个block类型的变量.
- 往这个变量中存储数据.
block类型的变量是专门用来存储 1段代码的.
- 这段代码可以有参数也可以返回值.
- 存储的代码是有限定的
- 声明变量的时候,必须指定这个变量的参数和返回值
声明block变量的语法
返回值类型 (^block变量名称)(参数列表);
void (^MyBlock)();
//代表声明了一个block类型的变量`^`
//变量名叫做`Myblock` .
//这个变量中只能存储没有返回值`(void)`
//也没有参数的代码`()`
==语法怪异之处在于,变量名在中间==
int (^myBlock)(int num1,int num2);
//代表声明了1个block类型的变量,变量名加做myBlock.
//这个变量中只能存储返回值为int类型的,并且有两个整型的参数的1段代码.
block变量的初始化
1. 初始化Block变量的原理:谢一段符合block变量要求的代码,把这段代码储存到这个block变量中
2. 书写一个block代码段的语法格式
^返回值类型(参数列表){
代码;
}
3. 无参数无返回值的代码段
^void(){
NSLog(@"我爱你");
NSLog(@"打瞌睡的小男孩");
};
//这个时候,就可以将这段代码 通过赋值符号. 赋值给无参数,无返回值的block变量.
void (^myBlock1)();
myBlock1 = ^void(){
NSLog(@"我爱你");
NSLog(@"打瞌睡的小男孩");
};
//代表的意义:myBlock1变量中存储了这段代码
4. 有返回值无参数的代码段.
==可以声明的同时赋值==
int (^Myblock2)() = ^int(){
int num1 = 10;
int num2 = 20;
int num3 = num1 + num2;
return num3;//如果代码段标识了有返回值,代码段结束之前必须要使用return返回一个数据
}
5. 有返回值有参数的代码段
^int(int num1,int num2){
int num3 = num1 + num2;
return num3;
}
- 特别强调:只能存储和这个block变量要求相同的代码段.否则就会报语法错误
执行存储在block变量中的代码
block();
有参数穿参数,有返回值就;
关于block的简写
如果一个代码段没有参数,那么这个代码段的小括弧可以省略.==这个不建议省略==!!!
声明block变量的小括弧是不可以少的
写代码段的时候,可以省略返回值类型,系统也是这么用的,可以省略.
系统根据返回的数据,自动判断返回值类型.没有返回就是
void
声明block变量的时候,如果要求存储的代码段有参数,这个时候,参数可以只写类型不写名称
int (^myBlock)(int,int);
但是代码段必须要同时些参数的类型和名称==我们在写block的时候,还是按照最标准的方式写比较好== 标准的总没错,没人会笑你写的标准.
简化block定义
- 隐隐感到不爽的地方:声明一个block变量代码太长了
- 想想办法将长的block定义变得短一些
typedef
也可以将一个长的block
类型,定义为1个短类型
为一个已经存在的数据类型,太长的时候,取一个别名.比如
typedef unsigned long NSUInteger
- 语法格式
typedef 返回值类型 (^新类型名称)(参数列表);
1. 举例typedef void (^NewType)();
.
2. 这时一个无参数无返回值的block类型
3. 如果我们要定义一个无参数无返回值的block变量,直接实用NewType就可以定义了.
4. 举例NewType b1;
block 访问外部变量
- 在block代码块的内部可以定义一个和外部的变量名称相同的变量
- 内部可以取出外部的全局变量和局部变量的值
- 并且还可以修改全局变量的值,但是不能修改局部变量的值.
- 如果就是要修改,给外面的局部变量加一个修饰符
__block
,比如__block int num = 10;
block作为函数的参数
- block是一个数据类型,那么block就必须可以作为方法/函数的参数
- 如何为函数写一个block类型的参数.
只需要在函数的小括弧中声明一个block类型的变量就可以.
void test (void (^block)()); //代表,这个函数有一个无参数无返回值的block参数.
如果你觉得晕.那么可以用typedef将block类型定义的短一点
typedef void (^NewType)(); void test(NewType b1)
- 如何调用带block参数的函数.
- 如果调用的函数的参数是一个block类型的.
那么就要求传递一个和形参的block**类型相同的block变量**.test(myBlock);
- 直接传递符合要求的代码段
objc
slmn bvc”wrap_width”: 80, //换行宽度(单位:字符)
test( ^{
代码段;
});
- 小技巧,打开Xcode提示的情况下,选中函数中的参数提示,按下回车,自动生成代码段的格式
- 能实现什么效果?
- 函数的惨速回能将调用者的数据传递到函数的内部去使用
- block作为参数:可以将调用者的一段代码传递到函数的内部去执行
- 什么时候需要将block作为参数?
1个函数代表1个相对独立的功能.
函数在完成这个功能的时候,发旋需要执行一段代码才可以据需完成
而这段代码函数内部不确定是什么样的代码
只有调用者确定
那么这个时候,就可以将block作为函数的参数
让调用者在调用这个函数的时候 把这段代码传递进来
放在这里执行
block与函数
- 相同点
- 都可以封装一段代码
- 不同点
- block是一个数据类型,可以声明变量.
- 函数就是一个函数
- block可以作为函数的参数
- 函数不能直接作为函数的参数
block作为函数的返回值
99%都不会作为函数或者方法的返回值,但是是可以的.
//这样写编译器都蒙圈了.
void (^)() test()
{
//如果非要作为返回值,就不要这样写,用typedef定义一下.
void (^b1)() = ^{
NSLog(@"hhhhaaa");
};
return b1;
}
协议
协议的基本使用
协议: protocol
1. 协议: 专门用来写方法的声明的,协议中不能写属性.
遵守了协议的类,这个类就拥有了这份协议中的所有的方法的声明,而不用自己去定义.
- 声明一个协议
@protocol 协议名称 <NSObject> 方法阿德声明 @end
- 协议中不能写属性.
- 可以写方法的声明
也可以写(开发中不会写)@property,但是协议中的@property只会生成get set的声明,没有实现.
- 类遵守协议.
如果一个类想要拥有某个协议中的所有的方法的声明,就只要遵守这个协议就可以了.
//遵守协议的格式 @interface 类名 : 父类名 <协议名称> // : 冒号 是继承 <>尖括号是遵守协议 @end
类遵守协议的效果:就是拥有协议的声明 ,不会自动实现.如果没有实现协议中的方法,编译的时候会给一个警告,执行的时候如果调用了没实现的方法,就会报错.如果父类遵守协议,相当于子类也继承了.
- 继承是单继承.协议是可以多遵守的
“`objc
@interface 类名 : 父类名 <协议名1,协议名2,……>
@end