首先要搞清楚weak属性的特点
weak策略表明该属性定义了一种“非拥有关系” (nonowning relationship)。
为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似;
然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)
那么runtime如何实现weak变量的自动置nil?
runtime对注册的类,会进行布局,会将 weak 对象放入一个 hash 表中。
用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会调用对象的 dealloc 方法,
假设 weak 指向的对象内存地址是a,那么就会以a为key,在这个 weak hash表中搜索,找到所有以a为key的 weak 对象,从而设置为 nil。
// 模拟下weak的setter方法,大致如下
- (void)setObject:(NSObject *)object
{
objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_ASSIGN);
[object cyl_runAtDealloc:^{
_object = nil;
}];
}
| Objective-C 对象的结构图 |
| --- |
| ISA指针 |
| 根类(NSObject)的实例变量 |
| 倒数第二层父类的实例变量 |
| ... |
| 父类的实例变量 |
| 类的实例变量 |
@implementation Son : Father
- (id)init
{
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end
* self 是类的隐藏参数,指向当前调用方法的这个类的实例。而 super 本质是一个编译器标示符,和 self 是指向的同一个消息接受者
* 当使用 self 调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;
* 而当使用 super时,则从父类的方法列表中开始找。然后调用父类的这个方法
* 调用[self class] 时,会转化成 objc_msgSend函数
```
id objc_msgSend(id self, SEL op, ...)
```
* 调用 [super class]时,会转化成 objc_msgSendSuper函数
```
id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
```
* 第一个参数是 objc_super 这样一个结构体,其定义如下
```
struct objc_super {
__unsafe_unretained id receiver;
__unsafe_unretained Class super_class;
};
```
* 第一个成员是 receiver, 类似于上面的 objc_msgSend函数第一个参数self
* 第二个成员是记录当前类的父类是什么,告诉程序从父类中开始找方法,找到方法后,最后内部是使用 objc_msgSend(objc_super->receiver, @selector(class))去调用, 此时已经和[self class]调用相同了,故上述输出结果仍然返回 Son
* objc Runtime开源代码对- (Class)class方法的实现
-(Class)class {
return object_getClass(self);
}
类方法
1 类方法是属于类对象的
2 类方法只能通过类对象调用
3 类方法中的self是类对象
4 类方法可以调用其他的类方法
5 类方法中不能访问成员变量
6 类方法中不能直接调用对象方法
7 类方法是存储在元类对象的方法缓存中
实例方法
1 实例方法是属于实例对象的
2 实例方法只能通过实例对象调用
3 实例方法中的self是实例对象
4 实例方法中可以访问成员变量
5 实例方法中直接调用实例方法
6 实例方法中可以调用类方法(通过类名)
7 实例方法是存放在类对象的方法缓存中
1.调用 -release :引用计数变为零
* 对象正在被销毁,生命周期即将结束.
* 不能再有新的 __weak 弱引用,否则将指向 nil.
* 调用 [self dealloc]
2\. 父类调用 -dealloc
* 继承关系中最直接继承的父类再调用 -dealloc
* 如果是 MRC 代码 则会手动释放实例变量们(iVars)
* 继承关系中每一层的父类 都再调用 -dealloc
3\. NSObject 调 -dealloc
* 只做一件事:调用 Objective-C runtime 中的 object_dispose() 方法
4\. 调用 object_dispose()
* 为 C++ 的实例变量们(iVars)调用 destructors
* 为 ARC 状态下的 实例变量们(iVars) 调用 -release
* 解除所有使用 runtime Associate方法关联的对象
* 解除所有 __weak 引用
* 调用 free()
```
// 启动RunLoop
[[NSRunLoop currentRunLoop] run];
```
* RunLoop的其他启动方法
```
// 第一个参数:指定运行模式
// 第二个参数:指定RunLoop的过期时间,即:到了这个时间后RunLoop就失效了
[[NSRunLoop currentRunLoop] runMode:kCFRunLoopDefaultMode beforeDate:[NSDate distantFuture]];
```
* 在子线程中通过scheduledTimerWithTimeInterval:...方法来构建NSTimer
* 方法内部已经创建NSTimer对象,并加入到RunLoop中,运行模式为NSDefaultRunLoopMode
* 由于Mode有timer对象,所以RunLoop就开始监听定时器事件了,从而开始进入运行循环
* 这个方法仅仅是创建RunLoop对象,并不会主动启动RunLoop,需要再调用run方法来启动
* 如果在主线程中通过scheduledTimerWithTimeInterval:...方法来构建NSTimer,就不需要主动启动RunLoop对象,因为主线程的RunLoop对象在程序运行起来就已经被启动了
```
// userInfo参数:用来给NSTimer的userInfo属性赋值,userInfo是只读的,只能在构建NSTimer对象时赋值
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(run:) userInfo:@"ya了个hoo" repeats:YES];
// scheduledTimer...方法创建出来NSTimer虽然已经指定了默认模式,但是【允许你修改模式】
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
// 【仅在子线程】需要手动启动RunLoop对象,进入运行循环
[[NSRunLoop currentRunLoop] run];
```
objc_autoreleasepoolPush
objc_autoreleasepoolPop
objc_aurorelease
// 创建队列组
dispatch_group_t group = dispatch_group_create();
// 获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 往队列组中添加耗时操作
dispatch_group_async(group, queue, ^{
// 执行耗时的异步操作1
});
// 往队列组中添加耗时操作
dispatch_group_async(group, queue, ^{
// 执行耗时的异步操作2
});
// 当并发队列组中的任务执行完毕后才会执行这里的代码
dispatch_group_notify(group, queue, ^{
// 如果这里还有基于上面两个任务的结果继续执行一些代码,建议还是放到子线程中,等代码执行完毕后在回到主线程
// 回到主线程
dispatch_async(group, dispatch_get_main_queue(), ^{
// 执行相关代码...
});
});
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
-(void)barrier
{
dispatch_queue_t queue = dispatch_queue_create("12342234", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});
// 在它前面的任务执行结束后它才执行,在它后面的任务等它执行完成后才会执行
dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3");
}
* objc是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector)
* 为了方便理解这个内容,还是贴一个objc的源代码
struct objc_class
{
// isa指针指向Meta Class,因为Objc的类的本身也是一个Object,
// 为了处理这个关系,runtime就创造了Meta Class,
// 当给类发送[NSObject alloc]这样消息时,实际上是把这个消息发给了Class Object
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父类
const char *name OBJC2_UNAVAILABLE; // 类名
long version OBJC2_UNAVAILABLE; // 类的版本信息,默认为0
long info OBJC2_UNAVAILABLE; // 类信息,供运行期使用的一些位标识
long instance_size OBJC2_UNAVAILABLE; // 该类的实例变量大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 该类的成员变量链表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定义的链表
// 方法缓存,对象接到一个消息会根据isa指针查找消息对象,
// 这时会在method Lists中遍历,
// 如果cache了,常用的方法调用时就能够提高调用的效率。
// 这个方法缓存只存在一份,不是每个类的实例对象都有一个方法缓存
// 子类会在自己的方法缓存中缓存父类的方法,父类在自己的方法缓存中也会缓存自己的方法,而不是说子类就不缓存父类方法了
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 协议链表
#endif
} OBJC2_UNAVAILABLE;
* objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类
* 然后在该类中的方法列表以及其父类方法列表中寻找方法运行
* 如果,在最顶层的父类中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX 。但是在这之前,objc的运行时会给出三次拯救程序崩溃的机会三次拯救程序崩溃的机会
* Method resolution
* objc运行时会调用+resolveInstanceMethod:或者 +resolveClassMethod:,让你有机会提供一个函数实现。
* 如果你添加了函数并返回 YES,那运行时系统就会重新启动一次消息发送的过程
* 如果 resolve 方法返回 NO ,运行时就会移到下一步,消息转发
* Fast forwarding
* 如果目标对象实现了-forwardingTargetForSelector:,Runtime 这时就会调用这个方法,给你把这个消息转发给其他对象的机会
* 只要这个方法返回的不是nil和self,整个消息发送的过程就会被重启,当然发送的对象会变成你返回的那个对象。
* 否则,就会继续Normal Fowarding。
* 这里叫Fast,只是为了区别下一步的转发机制。因为这一步不会创建任何新的对象,但Normal forwarding转发会创建一个NSInvocation对象,相对Normal forwarding转发更快点,所以这里叫Fast forwarding
* Normal forwarding
* 这一步是Runtime最后一次给你挽救的机会。
* 首先它会发送-methodSignatureForSelector:消息获得函数的参数和返回值类型。
* 如果-methodSignatureForSelector:返回nil,Runtime则会发出-doesNotRecognizeSelector:消息,程序这时也就挂掉了。
* 如果返回了一个函数签名,Runtime就会创建一个NSInvocation对象并发送-forwardInvocation:消息给目标对象
[UIView animateWithDuration:duration animations:^
{ [self.superview layoutIfNeeded]; }];
[[NSOperationQueue mainQueue] addOperationWithBlock:^
{ self.someProperty = xyz; }];
[[NSNotificationCenter defaultCenter] addObserverForName:@"someNotification"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification * notification)
{ self.someProperty = xyz; }];
* GCD
* 分析:self-->_operationsQueue-->block-->self形成闭环,就造成了循环引用
```
__weak __typeof__(self) weakSelf = self;
dispatch_group_async(_operationsGroup, _operationsQueue, ^
{
[weakSelf doSomething];
[weakSelf doSomethingElse];
} );
```
* NSNotificationCenter
* 分析:self-->_observer-->block-->self形成闭环,就造成了循环引用
```
__weak __typeof__(self) weakSelf = self;
_observer = [[NSNotificationCenter defaultCenter]
addObserverForName:@"testKey"
object:nil
queue:nil
usingBlock:^(NSNotification *note){
[weakSelf dismissModalViewControllerAnimated:YES];
}];
```
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。