本地对象在返回完成块之前变为零-目标C

时间:2019-07-20 03:19:00

标签: objective-c completionhandler retain-cycle

我有一个在函数内部声明的对象。该对象进行具有完成块的函数调用。功能正常执行。

此函数进行网络调用(在另一个类中)。从网络调用中获取结果后,我正在检查该类是否仍在内存中(使用weakSelf和strongSelf)

在此检查期间,它表明自己为零。

我知道如果我使用类方法或属性变量,可以解决此问题。但是有什么方法可以保留该对象(在函数内部声明)。 我尝试对该对象进行__strong和__block,但是不起作用。

这是我的代码

@implementation AViewController {
        -(void)viewWillAppear:(BOOL)animated {
            [super viewWillAppear:animated];
            [self methodA];
        }
        -(void)methodA {
            LocalClass *object = [LocalClass alloc] init];
            [object aMethodWithCompletionBlock:^(NSDictionay *result) {

            }];

        }
    }
    @implementation LocalClass {
        - (void)aMethodWithCompletionBlock:(void (^)(NSDictionay *result))completionHandler {

            __weak typeof(self) weakSelf = self;
            [NetworkClass methodToMakeRESTRequestOnComplete:^(NSDictionay *someResult) {
                __strong typeof(self) strongSelf = weakSelf;
            if(!strongSelf) { // this check fails as strongSelf is nil
                return;
            }

            //some code execution
            if (completionHandler != nil) {
                completionHandler(someModifiedResult);
            }
            }];
        }
    }

1 个答案:

答案 0 :(得分:1)

  

我知道如果我使用类方法或属性变量,可以解决此问题。但是有什么方法可以保留该对象(在函数内部声明)。我尝试使用__strong__block处理该对象,但无法正常工作。

您的代码正在尝试解决一个不存在的问题,这样做会创建一个问题。

weakSelf / strongSelf模式的目的是为了处理有害参考周期(并非所有参考周期都是有害的,确实有些是有用的)。仅当您确定存在这种有害循环时,才应使用它。

让我们看看您的代码,没有任何weakSelf / strongSelf的舞动:

 1 @implementation AViewController
 2 {
 3    -(void)methodA
 4    {  LocalClass *object = [LocalClass alloc] init];
 5       [object aMethodWithCompletionBlock:^(NSDictionay *result) { ... }];
 6    }
 7 }
 8 
 9 @implementation LocalClass
10 {
11    - (void)aMethodWithCompletionBlock:(void (^)(NSDictionay *result))completionHandler
12    {  [NetworkClass methodToMakeRESTRequestOnComplete:^(NSDictionay *someResult)
13       {
14          if (!self)
15             return;
16 
17          //some code execution
18          if (completionHandler != nil)
19             completionHandler(someModifiedResult);
20       }];
21    }
22 }

现在,当您拥有AViewController实例并调用methodA时会发生什么?

  1. 在第3行,将创建LocalClass的新实例,并将对它的引用存储在object中。 object的类型隐式为__strong LocalClass *,因此新实例对其有很强的引用,并且将继续存在。
  2. 在第5行,对aMethodWithCompletionBlock:引用的对象调用方法object,并将其传递给闭包。 请注意,methodAAViewController的实例都没有被调用来保留对此闭包的引用,它只是传递给方法。因此,在调用之后,不可能在属于methodA的局部变量或属于AViewController的实例变量与闭包之间建立引用循环。
  3. 在第12行,methodToMakeRESTRequestOnComplete:的方法NetworkClass被称为传递闭包

    • 此闭包引用了self,因此它包含对引用LocalClass的{​​{1}}实例的强引用
    • 这是在第3行创建的aMethodWithCompletionBlock:相同实例,因此现在对该对象有两个强大的引用。
    • 闭包还包含对由参数LocalClass引用的块的强引用
  4. 在第20行completionHandler返回,因为传递的块是完成块,所以不太可能被调用。因此,在这一点上,methodToMakeRESTRequestOnComplete:引用了该完成块,而完成块引用了NetworkClass实例。

  5. 在第21行,LocalClass返回。调用它的aMethodWithCompletionBlock:实例未保留对参数LocalClass的引用。
  6. 在第6行completionHandler返回。这将破坏其局部变量methodA,从而将强引用丢弃到其引用的object实例。此时系统可以考虑销毁该实例,但是,因为LocalClass对完成块有很强的引用,而对完成块有很强的引用NetworkClass实例仍然需要并且未被销毁。
  7. LocalClass调用其保留的块引用之后的某个将来的时间线14处。 NetworkClass变量强烈引用了最初在第4行创建的self实例,因此该实例仍然存在并且一切都很好。
  8. 在第20行,完成框返回。如果此时LocalClass放弃了对块的强引用,则可以(可能–假设没有其他对它的强引用)销毁该块。破坏消除了该块对其NetworkClassself所引用的对象的强引用,因此这些对象也可以(可能...)被破坏,并且最初在第4行创建的对象会咬住对象。灰尘。

没有有害的循环,不需要任何弱引用来管理它们。

HTH