#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__)
|