1.关联对象需要释放吗?
答:不需要,对象dealloc的时候已经帮我们移除关联对象了。

- 当对象
has_assoc关联的时候,会走下面的object_dispose

- 走
objc_destructInstance方法
- 有关联对象时,调用
_object_remove_assocations释放关联对象
2.类与category同名方法如何调用?
-
普通方法:因为分类的方法是在主类realize之后attach进去的,插在前面,所以优先调用分类的方法 (包括initialize) -
load方法:先调用主类的+load再调用分类的+load
+load方法顺序:
+[LRPerson load]
+[LRPerson(LRB) load]
+[LRPerson(LR) load]
-
打开
load_images源码
-
prepare_load_methods
递归调用schedule_class_load,将类的继承链添加至loadable_classes
loadable_classes是一个数组,数组中每个元素包含类和该类的load方法

add_category_to_loadable_list将category和每个category中的+load方法加入loadable_categories
- 回到
load_images中,调用call_load_methods
+load全部会调用,先走主类,再走分类;后添加的分类先走。
3.runtime是什么?
runtime是由C、C++、汇编实现的一套API,为OC语言加入了面向对象,运行时的功能。
runtime将数据类型的确定,由编译时推迟到了运行时
平时写的OC代码,在程序运行的过程中,其实最终会变成runtime的C语言代码
4.方法的本质,sel是什么?IMP是什么?两者之间有什么关系?
方法的本质:发送消息,有以下几个流程:
- 快速查找
imp,objc_msgSend在cache_t缓存中查找 - 慢速查找(
lookUpImpOrForward):递归父类,先找缓存再找method_list - 查找不到,动态方法决议
resolveInstanceMethod - 消息快速转发
forwardingTargetForSelector - 消息慢速转发
methodSignatureForSelector & forwardInvocation
sel就是方法编号,在read_images里就编译进了内存
imp是函数实现的指针,找imp就是找函数的过程:
- 首先知道
sel - 通过
sel找到imp - 通过
imp找到函数实现
5.能否向编译后得到的类中增加实例变量?能否向运行时创建的类中增加实例变量?
答案:
1.不能向编译后得到的类中增加实例变量
2.只要类还没有注册到内存中还是可以添加的
原因:编译好的类,实例变量存储在ro中,编译完成后,内存结构就完全确定,无法更改
可以添加属性和方法
6.[self class] 和[super class]的区别及原理分析
@interface LRPerson : NSObject
@end
@implementation LRPerson
- (instancetype)init {
if (self = [super init]) {
NSLog(@"%@ ---- %@",[self class],[super class]);
}
return self;
}
@end
打印结果:
LRPerson ---- LRPerson
[self class] :发送消息objc_msgSend;消息接收者是self,sel是class
[super class]:super 是编译器关键字 发送消息objc_msgSendSuper;消息接受者是self,sel是class
-
通过
clang编译,看super在底层的实现。
可以看出,他调用的是objc_msgSendSuper方法 -
搜索
objc_msgSendSuper
该方法有两个参数:
struct objc_super *superSEL op
- 查看
struct objc_super
在OBJC2环境下,只有两个变量:receiver和super_class
receiver :LRPerson
super_class:NSObject
查看arm64的汇编源码_objc_msgSendSuper
传入CacheLookup的参数 是LRPerson和NSObject
实际上就是LRPerson对象调用父类的- class方法
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
最终返回的还是LRPerson
重点 :以上的分析是错误的
在一下断点处,查看堆栈

Debug -> Debug Workflow -> Always Show Disassembly

走的其实是
objc_msgSendSuper2
- 搜索
objc_msgSendSuper2
注释的意思是在当前类查找,而不是superclass - 查看
objc_msgSendSuper2汇编
image.png
是在这里把p16指向了superclass
7.以下代码会输出什么?
@interface LRPerson : NSObject
@property (nonatomic,copy) NSString *name;
- (void)personMethod;
- (void)sayName;
@end
@implementation LRPerson
- (void)personMethod {
NSLog(@"%s",__func__);
}
- (void)sayName {
NSLog(@"%@",self.name);
}
@end
#import "ViewController.h"
#import "LRPerson.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Class cls = [LRPerson class];
void *lr = &cls;
[(__bridge id)lr personMethod];
[(__bridge id)lr sayName];
}
@end
打印结果:
-[LRPerson personMethod]
<ViewController: 0x7ffac4309100>
1.为什么(__bridge id)lr可以调用- personMethod?

上图为lr和一个LRPerson新建对象在内存中的情形,lr->cls->LRPerson与对象person的指向完全一致,欺骗了编译器。编译器错把lr当成是LRPerson对象,所有可以调用对象方法。
[(__bridge id)lr personMethod] 可以打印出-[LRPerson personMethod]
2.为什么第二个打印的是ViewController?
name是对象里的第二个变量,取name是从isa向下偏移8字节。

- 进入
- (void)viewDidLoad函数,会先把两个隐藏参数压入栈中;先压入self,再压入sel - 调用
[super viewDidLoad];会存入struct objc_super结构体,结构体有两个变量:receiver、class
super关键字使用的是objc_msgSendSuper2方法,class是当前类,也就是viewController,receiver是self当前viewController
结构体压栈:最后面的先进栈
- 当调用方法取
self.name的时候,就是lr指针向上平移8个字节,取到了当前viewController,故打印<ViewController: 0x7ffac4309100>











