散列表的结构分析
- slock : 锁
- refcnts : 引用计数表
- weak_table : 弱引用计数表 被__weak 修饰的对象会加入到这个表中。
weak
__weak typeof(id) weakObjc = objc; 修饰时
-
objc_initWeak 弱引用创建
location 是weakObjc的指针地址
-
storeWeak
从相关的散列表中找到相关对象所存放的弱引用表。
-
weak_register_no_lock
链表结构添加节点,或者扩容后添加节点。
-
weak_grow_maybe
扩容处理
-
weak_entry_insert
插入节点。
-
weak_entry_t
-
流程概述 1.⾸先我们知道有⼀个⾮常⽜逼的家伙-sideTable
2:得到sideTable的weakTable 弱引⽤表
3:创建⼀个weak_entry_t
4:把referent加⼊到weak_entry_t的数组inline_referrers
5:把weak_table扩容⼀下
5:把new_entry加⼊到weak_table中
弱引用对象的引用计数分析
- objc_loadWeakRetained
获取原来的对象地址,赋值给这个临时变量。这里还是原来地址的引用计数 1.
这里会引用计数再次加一。
所以弱引用的引用计数会是2.由于这个方法中的obj和result是临时变量没有保存操作,所以弱引用的引用计数都是2.
1. 使用CADisplayLink、NSTimer有什么注意点?
CADisplayLink保证调用频率和屏幕的刷帧一致,60FPS。
CADisplayLink、NSTimer都会对target进行引用,很容易造成循环引用得问题,造成target无法释放。
**
self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(linkTest)];
[self.link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
》》》》》》》》》》》》》》》》》》
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(timerTest) userInfo:nil repeats:YES];
解决方法:
1. timer使用block方法,来对target进行弱引用。
2. 使用一个中间类,弱引用target,然后用消息转发再次转发给target。因为中间类跟target是弱引用,当vc delloc的时候,会正常进行释放,timer也会释放,避免了循环引用得问题。
**
//JWHelper继承NSObject
#import "JWHelper.h"
@interface JWHelper()
@property (nonatomic, weak) id target;
@end
@implementation JWHelper
+ (instancetype)initHelperTarget:(id)target {
JWHelper * helper = [JWHelper new];
helper.target = target;
return helper;
}
//消息转发给target
- (id)forwardingTargetForSelector:(SEL)aSelector {
return self.target;
}
@end
>>>>>>>>>>>>>>>>>>>调用>>>>>>>>>>>>>>>>>>>>>
self.link = [CADisplayLink displayLinkWithTarget:[JWHelper initHelperTarget:self] selector:@selector(linkTest)];
[self.link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target: [JWHelper initHelperTarget:self] selector:@selector(timerTest) userInfo:nil repeats:YES];
NSProxy类
在OC中,NSProxy类属于NSObject同级别得基类,NSProxy主要作用就是负责消息转发机制,当转发到的selector之后会直接调用
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
-(void)forwardInvocation:(NSInvocation *)anInvocation
两个方法进行消息转发机制,该类转发效率比NSObject效率高,因为他不会从缓存类/superClass内部寻找方法,如果没有找到再进行消息转发。它是直接进行消息转发,所以效率更高。
**
//JWProxy继承NSProxy
@interface JWProxy()
@property (nonatomic, weak) id target;
@end
@implementation JWProxy
+ (instancetype)initHelperTarget:(id)target {
JWProxy * proxy = [JWProxy alloc];
proxy.target = target;
return proxy;
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [self.target methodSignatureForSelector:aSelector];
}
-(void)forwardInvocation:(NSInvocation *)anInvocation {
[anInvocation invokeWithTarget:self.target];
}
@end
NSTimer、CADisplayLink是依赖RunLoop实现的,当每次执行一圈RunLoop系统会计算是否满足触发定时器的条件,所以使用RunLoop触发定时器有误差,造成不准时,如果对精度比较高使用GCD定时器。
**
//GCD创建定时器
@interface JWGCDTimer()
@property (nonatomic, strong) dispatch_source_t timer;
@end
@implementation JWGCDTimer
+ (instancetype)initTimer:(NSTimeInterval)start interval:(NSTimeInterval)interval handler:(void (^)(void))block {
return [[JWGCDTimer alloc]initTimer:start interval:interval handler:block];
}
- (instancetype)initTimer:(NSTimeInterval)start interval:(NSTimeInterval)interval handler:(void (^)(void))block {
if (self = [super init]) {
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, start * NSEC_PER_SEC, interval * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
if (block) {
block();
}
});
dispatch_resume(timer);
self.timer = timer;
}
return self;
}