面试题
问题1:Category的使用场合是什么?
答复:
为某个类拓展方法,分模块
问题2:Category的实现原理
答复:
Category编译之后的底层是Struct Category_t
,里面存储着分类的对象方法、类方法、属性、协议信息
在程序运行的时候,runtime会将Category的数据,合并到类信息中(类对象、元类对象中)
image.png
问题3:Category和Class Extension的区别是什么?
答复:
Class Extension在编译的时候,它的数据就已经包含在类信息中 Category是在运行时,才会将数据合并到类信息中
问题4:Category中有load方法吗?load方法是什么时候调用的?load方法能继承吗?
答复:
有load方法
load 方法在runtime加载类、分类的时候调用
load 方法是通过函数地址直接调用的,其他的方法是通过消息机制调用,也就是通过isa指针层层寻找方法
load 方法可以继承,但是一般情况下不会主动去调用load方法,都是让系统主动调用(备注:继承后,手动子类调用load方法,是通过消息机制执行的,那么就会通过isa去类中查找,如果类没有就会去父类查找)
问题5:load、initialize方法的区别是什么?它们在category中的调用顺序?以及出现继承时它们之间的调用过程?
答复:
1、调用方式
- load是根据函数地址直接调用的
- initialzer是通过objc_msgSend调用
2、调用时刻
- load是runtime加载类、分类的时候调用(只会调用1次)
- initialize是类第一次接收到消息的时候调用,每个类只会initialize一次(父类的initialize方法可能会被调用多次)
3、load、initialeze的调用顺序
load
(1)先调用类的load
- 按编译的先后顺序调用
- 调用子类的+load之前会先调用父类的+load
(2)再调用分类的load- 按编译的先后顺序调用
initialize
- 先初始化父类
- 再初始化子类(可能最终调用的是父类的initialize方法)
问题6:Category能否添加成员变量?如果可以,如何给Category添加成员变量?
答复:
知识点:
-
通过runtime动态将分类的方法合并到类对象、元类对象中
-
分类编译后,会转变为_category_t结构体
image.png
*类扩展(也叫做匿名分类)
+load
- +load 方法会在runtime加载类、分类时调用
- 每个类、分类的+load,在程序运行过程中
只调用一次
调用顺序
1、先调用类的+load
- 按照编译先后顺序调用(先编译,先调用)
- 调用子类的+load之前会先调用父类的+load
2、再调用分类的+load
- 按照编译先后顺序调用(先编译,先调用)
+initialize方法
+initialize方法会在
类
第一次接收到消息时调用调用顺序
先调用父类的+initialize,再调用子类的+initialize
(先初始化父类,再初始化子类,每个类只会初始化1次)
+关联对象
关联对象的主要作用就是给分类添加属性
首先平时给一个类添加属性,做了哪几件事?比如给Person类添加一个age属性
(1)添加成员变量:_age
(2)声明并实现getter和setter方法
@interface MJPerson : NSObject
@property (nonatomic, assign, readwrite) NSInteger age;
// ========= 内容实现的内容 =========
{
NSInteger *_age;
}
- (NSInteger)age;
- (void)setAge:(NSInteger)age;
// ========= 内容实现的内容 =========
@end
#import "MJPerson.h"
@implementation MJPerson
// ========= 内容实现的内容 =========
- (NSInteger)age {
return _age;
}
- (void)setAge:(NSInteger)age {
_age = age;
}
// ========= 内容实现的内容 =========
@end
那在分类中添加属性,到底有做了哪些了?比如给Person的分类Person(test)添加属性name
分类中直接添加属性,系统只对getter 和 setter 做声明,并不对其实现,而且也不会生成成员变量
@interface MJPerson (test)
@property (nonatomic, copy, readwrite) NSString *name;
// ========= 内容实现的内容 =========
- (NSString * _Nonnull)name;
- (void)setName:(NSString * _Nonnull)name;
// ========= 内容实现的内容 =========
@end
#import "MJPerson+test.h"
#import <AppKit/AppKit.h>
@implementation MJPerson (test)
@end
既然系统没有实现,那么只要我们手动将成员变量、setter方法和getter方法的实现添加就可以了,但是分类的编译结构体category_t中并没有成员变量的属性,所以不能手动添加成员变量存储属性值
,那就需要用到上面讲到的“关联对象”
Person分类添加属性"name"
#import "MJPerson.h"
@interface MJPerson (test)
@property (nonatomic, copy, readwrite) NSString *name;
@end
#import "MJPerson+test.h"
#import <objc/runtime.h>
@implementation MJPerson (test)
- (void)setName:(NSString *)name {
objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
//_cmd代表当前的@selector方法
//_cmd = @selector(name)
return objc_getAssociatedObject(self, @"name");
}
@end
关联对象原理
- 关联对象并不是存储在关联对象本身内存中
- 关联对象存储在全局的统一的一个
AssociationsManager
中设置关联对象为nil,就相当于是移除关联对象
关联对象原理
网友评论