【问题标题】:Confusing deinit behavior in SwiftSwift 中令人困惑的 deinit 行为
【发布时间】:2016-01-07 11:06:33
【问题描述】:

在我的代码中,我有一个 APIManager 和它的子类 ArticleAPIManager 在我实现的两个类中 deinit { print("deinit className") }

我试图查看是否会立即释放以下代码:

ArticleManager().fetchArticlesWithParameters(ArticlesParameters.defaultParameters()) { (articlesData, error) -> Void in

        print("Done")
    }

控制台显示如下:

deinit ArticleAPIManager

deinit APIManager

Done

如果 Manager 之前被释放,完成处理程序如何仍然存在?

func fetchArticleWithParameters<R:xxxProtocol>(parameters:R , completionHandler: ArticleCompletionHandler) {

        if let articleURLWithParamsURL = params.endPointURL() {

        fetchURL(articleURLWithParameters) { (jsonData, error) -> Void in

            guard let jsonData = jsonData else {
                completionHandler(articlesData: nil, error: error)
                return
            }

            if let rawArray = jsonData["data"] as? [APIJSON] {
                let articles = APIGenericResponceParser<T>().parseArray(rawArray)

                completionHandler(articlesData: articles, error:nil)

            }

            }//fetchURL
        }//iflet-NSURL

}

【问题讨论】:

  • 你的fetchArticlesWithParameters看起来怎么样?
  • 这里缺少你的 api 实现。但通常您的闭包回调由异步 api 捕获以供稍后调用。(例如:调度后)。因此,您的班级首先要删除。当异步 API 触发时,回调执行
  • @AntonBronnikov 我更新了问题

标签: ios swift networking memory-management nsurlsession


【解决方案1】:

试图通过一个简单的例子来解释:

class Test {

    let someProperty = "Very nice property"

    func printWithDelay() {

        //Prints done after 3 seconds
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(NSEC_PER_SEC * 3)), dispatch_get_main_queue()) {
            print("Done")
        }
    }

    deinit {
        print("\(self) is deinited")
    }
}

并使用此代码:

var test: Test? = Test()
test?.printWithDelay()
test = nil

在此示例中,dispatch_after块不引用任何 Test属性因此它不会保留它,这就是为什么你会看到

Test is deinited
Done

在控制台中,就像在您的示例中一样。但是,如果您在printWithDelay 中打印someProperty,例如:

func printWithDelay() {

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(NSEC_PER_SEC * 3)), dispatch_get_main_queue()) {
        print(self.someProperty)
    }
}

然后你会看到

Very nice property
Test is deinited

在控制台中,由于dispatch_after 的块保留self 并且在块执行之前不会让它被取消。

【讨论】:

  • 正如你所说的dispatch_after 保留self。所以如果 self 被保留,为什么要调用 deinit ?不应该是 self.retainCount == 0 才能调用 deinit 吗?
  • @Bobj-C 在第一个例子中,self 没有被保留,这就是它被释放的原因。在我写的printWithDelay的第二个版本中,self 被保留了。
【解决方案2】:

闭包是 Swift 中的引用类型。您可以将它们从一个地方传递到另一个地方,就像任何其他“普通”对象一样。

来自documentation

每当您将函数或闭包分配给常量或变量时,您实际上是将该常量或变量设置为函数或闭包的引用...这也意味着,如果您将闭包分配给两个不同的常量或变量,这两个常量或变量都将引用同一个闭包:

在您的情况下,您将completionHandler 传递给fetchURL(通过endPointURL),然后它被fetchURL 捕获并保持活动状态,即使传递它的ArticleAPIManager 的对象已经死了.获取完成后,将执行完成处理程序。在您的情况下,它已经发生在 ArticleAPIManager 的对象被取消初始化之后。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-22
    • 1970-01-01
    相关资源
    最近更新 更多