NSTimer、CADisplayLink会对target产生强引用,如果target同时对他们产生强引用,则会发生循环引用。
以NSTimer为例,解决循环引用的问题。
方法1:使用block
- - (void)viewDidLoad {
- [super viewDidLoad];
- // Do any additional setup after loading the view.
-
- __weak typeof(self) weakself = self;
- self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
- [weakself func];
- }];
- }
- - (void)func
- {
- NSLog(@"%s",__func__);
- }
- - (void)dealloc
- {
- NSLog(@"%s",__func__);
- [self.timer invalidate];
- }
方法2:使用NSObject作为中间对象
- Proxy1.h
- @interface Proxy1 : NSObject
- + (instancetype)initWithTarget:(id)target;
- @end
- Proxy1.m
- @interface Proxy1 ()
- @property (nonatomic,weak) id target;
- @end
-
- @implementation Proxy1
- + (instancetype)initWithTarget:(id)target
- {
- Proxy1 *proxy = [[Proxy1 alloc] init];
- proxy.target = target;
- return proxy;
- }
- - (id)forwardingTargetForSelector:(SEL)aSelector
- {
- return self.target;
- }
- @end
- - (void)viewDidLoad {
- [super viewDidLoad];
- // Do any additional setup after loading the view.
-
- self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[Proxy1 initWithTarget:self] selector:@selector(func) userInfo:nil repeats:YES];
- }
- - (void)func
- {
- NSLog(@"%s",__func__);
- }
- - (void)dealloc
- {
- NSLog(@"%s",__func__);
- [self.timer invalidate];
- }
方法3:使用NSProxy作为中间对象
- Proxy2.h
- @interface Proxy2 : NSProxy
- + (instancetype)initWithTarget:(id)target;
- @end
- Proxy2.m
- @interface Proxy2 ()
- @property (nonatomic,weak) id target;
- @end
-
- @implementation Proxy2
- + (instancetype)initWithTarget:(id)target
- {
- Proxy2 *proxy = [Proxy2 alloc];
- proxy.target = target;
- return proxy;
- }
- - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
- {
- return [self.target methodSignatureForSelector:sel];
- }
- - (void)forwardInvocation:(NSInvocation *)invocation
- {
- [invocation invokeWithTarget:self.target];
- }
- @end
- - (void)viewDidLoad {
- [super viewDidLoad];
- // Do any additional setup after loading the view.
-
- self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[Proxy2 initWithTarget:self] selector:@selector(func) userInfo:nil repeats:YES];
- }
- - (void)func
- {
- NSLog(@"%s",__func__);
- }
- - (void)dealloc
- {
- NSLog(@"%s",__func__);
- [self.timer invalidate];
- }
方法3的优点:
执行效率高,无需执行父类的方法搜索过程,直接进行消息转发。
关于NSProxy补充:
通过调用isKindOfClass
- Proxy1 *proxy1 = [Proxy1 initWithTarget:self];
- Proxy2 *proxy2 = [Proxy2 initWithTarget:self];
- NSLog(@"%d",[proxy1 isKindOfClass:[ViewController class]]); // 0
- NSLog(@"%d",[proxy2 isKindOfClass:[ViewController class]]); // 1
proxy1为Proxy1类型,Proxy1继承自NSObject,可以正常处理isKindOfClass方法,所以判断结果为0.
proxy2为Proxy2类型,Proxy2继承自NSProxy,大部分方法会直接进入消息转发阶段,会改为使用target进行调用,所以判断结果为1.
通过观察NSProxy的源码发现,该方法直接进行了消息转发。
- /**
- * Calls the -forwardInvocation: method to determine if the 'real' object
- * referred to by the proxy is an instance of the specified class.
- * Returns the result.<br />
- * NB. The default operation of -forwardInvocation: is to raise an exception.
- */
- - (BOOL) isKindOfClass: (Class)aClass
- {
- NSMethodSignature *sig;
- NSInvocation *inv;
- BOOL ret;
-
- sig = [self methodSignatureForSelector: _cmd];
- inv = [NSInvocation invocationWithMethodSignature: sig];
- [inv setSelector: _cmd];
- [inv setArgument: &aClass atIndex: 2];
- [self forwardInvocation: inv];
- [inv getReturnValue: &ret];
- return ret;
- }