弱引用表浅析

170 阅读3分钟

散列表的结构分析

image.png

  • slock : 锁
  • refcnts : 引用计数表
  • weak_table : 弱引用计数表 被__weak 修饰的对象会加入到这个表中。

weak

__weak typeof(id) weakObjc = objc; 修饰时

  • objc_initWeak 弱引用创建 image.png location 是weakObjc的指针地址

  • storeWeak image.png 从相关的散列表中找到相关对象所存放的弱引用表。

  • weak_register_no_lock image.png

image.png 链表结构添加节点,或者扩容后添加节点。

  • weak_grow_maybe image.png 扩容处理

  • weak_entry_insert image.png 插入节点。

  • weak_entry_t image.png

  • 流程概述 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 image.png 获取原来的对象地址,赋值给这个临时变量。这里还是原来地址的引用计数 1. image.png

image.png 这里会引用计数再次加一。 所以弱引用的引用计数会是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;
}