#pragma mark - 0_18 分类的使用注意 [掌握]
1.主线任务 - 分类的注意
分类文件的命名:
本类名+分类名.h
本类名+分类名.m
分类的使用注意:
1.分类中不能添加成员变量,但是可以访问本类中的@interface中定义的成员变量,但是不能访问本类中私有的成员变量
2.分类中使用@property会有什么效果?
1.会生成对应的getter和setter的声明
2.不会生成_开头的成员变量
3.不会生成对应getter和setter的实现
3.分类可以通过self调用本类的方法
4.如果在本类中使用@property声明一个属性,那么在分类中不能访问对应的_成员变量,但是可以调用对应的setter和getter
5.可以在分类中重写本类中同名的方法
1.如果重写,编译器会有一个警告,提示在分类中实现了和本类中同名的方法
2.一旦重写,不管是否引入分类的头文件,都会优先调用分类中该方法的实现
6.如果在多个分类中同时重写了本类中的方法,那么会优先调用最后参与编译的分类中对该方法实现
7.方法的调用顺序
(最后参与编译的)分类 -> 本类 -> 父类
分类的总结:
1.分类的作用:
将1个类分成多个模块,方便维护和管理
2.什么时候需要用到分类
1.团队合作开发的时候,需要把一个非常庞大的类,分给多个人去写
2.当1个类过于臃肿庞大的时候,可以拆分成多个小的模块
3.分类的好处:
将1个复杂的类,分成多个模块,方便维护
2.支线任务 - 关联对象
使用分类存在的问题:
1.分类中不能添加成员变量
2.如果是自定义的类,可以直接在本类中添加成员变量,然后在分类中使用
3.如果是系统的类,想在分类中添加成员变量,如何做? -> 使用runtime中的关联对象
如何使用:
导入<objc/runtime.h>
// 设置关联对象
// object 说明给哪个对象添加关联对象
// key 说明通过key保存值
// value 保存的值
// policy 对于关联对象的内存管理策略
// 作用:把 self 和 对象 通过 key 关联在一起
void objc_setAssociatedObject(id object, const void *key, idvalue, objc_AssociationPolicy policy);
// 获取关联对象
// object 说明获取哪个对象的关联对象
// key 说明通过key获取值
// 作用:根据 key 获得和 self 相关联的 对象
id objc_getAssociatedObject(id object, const void *key);
4.关联对象的本质
1.关联对象,并不是给对象真实增加一个成员变量,所以并没有在对象的堆区空间内,定义一个变量
2.在调用该函数的时候,会在全局区保存关联对象的哈希表中,根据对象的地址,创建一个和该对象关联的哈希表,然后在该表中,根据key存储对指定的对象
3.当对象释放的时候,系统会自动的处理和他关联的对象,不需要我们管理
#pragma mark - 0_19 非正式协议 [掌握]
非正式协议:给系统的类添加的分类叫做非正式协议
作用:给系统的类扩充新的方法
NSString + test
NSObject + test
#pragma mark - 02 延展的基本使用 [掌握]
1.延展是什么: Extension
1.延展是一个匿名的分类
2.延展只有声明,没有单独的实现,和本类共享同一个实现
2.延展的声明
@interface HMPerson ()
@end
延展没有单独的实现.和本类共享1个实现
将方法声明写在延展中 将方法的实现写在本类的实现中.
3.延展的作用
1.可以延展中添加成员变量
2.延展中使用@property定义属性会做几件事情? (和在类的定义中使用@property是一样的)
4.添加1个延展
延展只有1个.h文件. 没有.m文件. 因为延展没有单独的实现.和本类共享1个实现.
100%的情况下,延展不会独占1个头文件. 延展都是写在类的.m文件中.
#pragma mark - 03 延展的使用场景 [掌握]
1.延展可以理解为类的私有声明
1.在类的声明可以做的事情,在延展中都可以做
2.写在延展中的成员变量和方法,只能当前类的内部访问,不能在外部访问
2.延展的最常见用法:
定义私有的@property
3.在就业班以及工作中的使用
1.给系统的类写分类
1.分类的作用:在不修改和继承自一个类的基础上,给该类增加新的方法
2.给系统的类写分类,扩展新的常用的必须的方法
3.把一些频繁用到的功能,写成分类,多积累
2.延展(类的私有扩展),在其中定义只希望在当前类的内部访问的成员变量和方法.
#pragma mark -4. 延展的使用注意.
1).分类中只能新增方法.可以写@property但是只会生成getter setter的声明.
2).延展中可以写属性.
也可以写@property. 会自动的生成私有属性,getter setter的声明. getteer setter的实现.
方法声明肯定也是可以的.
3).
延展天生就是来:私有化类的成员的.
如果类中有成员需要被私有化.
属性需要被私有化:
a. 在本类的@interface中@private
b. 在本类的@implementation中.
c. 写在延展中.
必须将其写在延展中.
方法需要被私有化:只写实现不写声明.
建议: 私有方法仍然要写声明和实现,只不过声明写在延展中.
@property需要被私有化.把@property写在延展中.
#pragma mark - 04 block变量的声明 [掌握]
点招会考
就业班会用
找工作面试也会用
在实际开发中频繁用到
1.block是什么
是一个OC中特有的数据类型
2.有什么作用
保存一段代码,在需要的时候执行
3.怎么用
如何声明int类型的变量
int a;
如何使用block类型声明一个变量
注意点:在使用block类型声明变量的时候,需要说明block变量中保存的代码段的类型
代码段的类型:是否有参数,是否返回值
1.声明一个 能够保存无参数无返回值的代码段 的block类型变量
// block的变量名:myBlock
// block变量中保存的代码段类型:void (^)()
void (^myBlock)();
2.声明一个 能够保存无参数有返回值的代码段 的block类型变量
// block的变量名:myBlock
// block变量中保存的代码段类型:int (^)()
int (^myBlock)();
3.声明一个 能够保存有参数无返回值的代码段 的block类型变量
// block的变量名:myBlock
// block变量中保存的代码段类型:void (^)(int a)
void (^myBlock)(int a);
4.声明一个 能够保存有参数有返回值的代码段 的block类型变量
// block的变量名:myBlock
// block变量中保存的代码段类型:int (^)(int a)
int (^myBlock)(int a);
4.OC中的block类似于C语言中的函数指针
回顾:声明一个能够指向有参数有返回值函数的函数指针
int (*p)(int a, int b);
#pragma mark - 05 block变量的初始化和使用 [掌握]
1.block类型的变量有什么作用?
保存一段代码,这段代码可以有参数也可以返回值.
注意点:这段代码需要和声明block变量时指定的 能够保存的代码段类型 匹配
2.定义代码段
1.定义一个 无参数无返回值 代码段
^void () {
NSLog(@"hello world...");
};
2.定义一个 无参数有返回值 代码段
^int () {
NSLog(@"hello world...");
return 10;
};
3.定义一个 有参数无返回值 代码段
^void (int a) {
NSLog(@"a = %d", a);
};
4.定义一个 有参数有返回值 代码段
^int (int a) {
return a+1;
};
3.如何把代码段赋值给block变量
void (^myBlock)() = ^void () {
NSLog(@"hello world...");
};
格式规范:
返回值类型 (^block变量名称)(参数列表) = ^返回值类型 (参数列表) { ///执行的代码 };
4.声明一个block类型的变量之后,其中保存的代码段会立刻执行吗?
不会
在手动调用的时候执行
与参传参, 有返回值就接.
#pragma mark - 06 block的简写 [掌握]
返回值类型 (^block变量名称)(参数列表) = ^返回值类型 (参数列表) { ///执行的代码 };
1.针对定义代码段的简写
1.如果一个代码段没有参数,可以省略小括号
2.代码的返回值类型可以直接省略,编译器会自动检测
2.针对声明block变量的简写
参数列表中只需要说明参数的类型和参数的数量,不需要说明参数的名称
3.建议:使用完整的规范的格式
#pragma mark - 07 使用typedef简化block定义 [掌握]
1.typedef的作用:给一个类型起别名
2.使用typedef给int类型起别名
typedef int MyInt;
typedef 原类型名 新类型名;
3.使用typedef给block类型起别名:
// block的名称:myBlock
// block的类型:int (^)(int a) -> 有一个int类型参数并且返回值是int类型的block
int (^myBlock)(int a);
// 给具有一个参数一个返回值的block类型起别名
// 类型的别名: myBlock
// block类型: int (^)(int) -> 有一个int类型参数并且返回值是int类型的block
typedef int (^myBlock)(int a);
myBlock block1;
myBlock block2;
和使用typedef给函数指针起别名写法相同
3.建议:
1.在初学block的时候,可以使用typedef简化block的定义
2.在公司实际开发中,很少使用typedef
3.不建议使用,尽量自己写,多练习
#pragma mark - 08 block访问外部变量的问题 [掌握]
1.在block内部可以访问并直接修改全局变量的值
2.在block内部可以访问但是不能直接修改局部变量的值
如果在block内部想修改局部变量的值,需要对该变量使用__block进行修饰
1.在Block内部访问外部的全局变量
1.变量的地址没有任何改变;
2.变量的值可以在Block内部直接修改;
2.在Block内部访问外部的局部变量
1.外部的局部变量地址是栈区的
2.在block中访问的时候,会把该变量拷贝到堆区,以常量的形式进行拷贝,所以不能直接修改该变量的值
3.在Block内部不能直接修改外部局部变量的值
3.如果想在block内部修改外部局部变量的值,需要使用__block对该变量进行修饰
1.在block内部访问该变量之前,该变量地址是栈区的
2.在block内部访问改变量的时候,会把它拷贝到堆区,以指针的形式进行拷贝,可以修改该变量的值
3.在block内部访问之后,该变量地址仍然是堆区的
4.如果在block内部定义了一个和外部变量同名的局部变量
1.会屏蔽对外部变量的访问
2.会在栈区创建一个新的局部变量
5.Block变量到底存在哪个区域
OC对象是存在堆区的
局部变量是存在栈区的
在MRC中
1.如果在block内部没有访问外部的局部变量,那么block是存储在全局区的
2.如果在block内部访问了外部的局部变量,那么block是存储在栈区的
在ARC中
1.如果在block内部没有访问外部的局部变量,那么block是存储在全局区的(__NSGlobalBlock__)
2.如果在block内部访问了外部的局部变量,那么block是存储在堆区的(__NSMallocBlock__)
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) | 黑马程序员IT技术论坛 X3.2 |