评论

收藏

[iOS开发] [iOS]定时器NSTimer、CADisplayLink的内存管理

移动开发 移动开发 发布于:2021-06-25 09:43 | 阅读数:504 | 评论:0

  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;
}
关注下面的标签,发现更多相似文章