【问题标题】:self reference swift closure自引用快速关闭
【发布时间】:2015-02-27 03:53:10
【问题描述】:

我在 swift 中有一个惰性属性,它有一个看起来像这样的回调。

lazy var apiClient: MyApiClient =
    {

        var apiClient : MyApiClient = MyApiClient()

        apiClient.detailSearchFinishedCallBack = {
            (detailModel : DetailModel!) in

        }

        return apiClient
    }()

我想在闭包内访问另一个延迟加载属性,如下所示:

  lazy var loadingView : LoadingView =
    {
        var loadingView : LoadingView = LoadingView()
        loadingView.frame = CGRectMake(0, 0, 200, 200)
        loadingView.center = self.view.center

        return loadingView
    }()

但是,我无法引用闭包内的加载视图。在目标 c 中,这看起来像这样。

-(LoadingView *)loadingView
{
    if(!_loadingView)
    {
        _loadingView = [LoadingView new];
        _loadingView.frame = CGRectMake(0, 0, 200, 200);
        _loadingView.center = self.view.center;
    }
    return _loadingView;
}

-(MyApiClient *)apiClient
{
    if(!_apiClient)
    {
        _apiClient = [MyApiClient new];

        __weak FeedViewController *_self = self;

        self.apiClient.detailSearchFinishedCallBack = ^(DetailModel  *detailModel)
        {
              [_self.loadingView stopAnimating];
        };
    }

    return _apiClient;
}

有人愿意向我展示 Swift 中的等价物吗?

更新:

  lazy var apiClient: MyApiClient = {
            let apiClient: MyApiClient = MyApiClient()
            apiClient.detailSearchFinishedCallBack = { [weak self] (detailModel: DetailModel!) in

                println(self?.loadingView.frame)

                return
            }
            return apiClient
            }()

所以我继续尝试实施建议的解决方案,但出现编译错误。具体来说,我遇到了这个错误:

命令 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc 失败,退出代码为 1

我不确定这是否与我在项目中的 swift 配置有关,但我正在使用桥接头导入我的目标 c 头文件以暴露给 Swift。我想不出任何其他可能导致此问题的原因,但我们将不胜感激任何帮助来解决此问题。

【问题讨论】:

  • @MikeTaverne 如果您引用其他文章的目的是解决weakSelf 模式,我认为这不是这里的关键问题。我认为问题在于在引用另一个属性的闭包中配置了变量的默认值。就个人而言,当这种相互依赖存在时,我放弃了默认值的闭包,将其移至init 和/或viewDidLoad 酌情。这样就绕过了问题。
  • @Rob 我相信你对此的判断。我自己没有尝试过,但你的解释是有道理的。谢谢。
  • 更新确实提出了“自我”指的是什么问题。它可能应该是 apiClient 并且无权访问“loadingView”,不是吗?
  • @zic10 顺便说一句,您提到了编译器崩溃。是的,当使用闭包为属性提供默认值时,我注意到了隐秘且莫名其妙的崩溃。更糟糕的是,在我的测试中(使用单例模式),问题并不总是可复制的。我不认为 Swift 将闭包作为默认值的实现非常健壮。

标签: ios objective-c xcode swift


【解决方案1】:

语法是:

lazy var apiClient: MyApiClient = {
    let apiClient: MyApiClient = MyApiClient()
    apiClient.detailSearchFinishedCallBack = { [weak self] detailModel in
        self?.loadingView?.stopAnimating()
    }
    return apiClient
}()

在我最初回答这个问题时,Swift 编译器在解释上述语法时遇到了问题,所以我提出了下面概述的以下丑陋的解决方法。但您现在可以使用上述简单、合乎逻辑的语法。


原答案:

您正在使用闭包为属性提供默认值。但是在 Swift 编程语言Initialization 章节的 Setting a Default Property Value with a Closure 或 Function 部分中说:

注意:如果您使用闭包来初始化属性,请记住在执行闭包时实例的其余部分尚未初始化。这意味着您不能从闭包中访问任何其他属性值,即使这些属性具有默认值。您也不能使用隐含的self 属性,或调用任何实例的方法。

话虽如此,人们可以提出一个论点,即编译器应该允许惰性变量,但事实并非如此。


你说你想要这个 Objective-C 代码的实现:

-(MyApiClient *)apiClient
{
    if(!_apiClient)
    {
        _apiClient = [MyApiClient new];

        __weak FeedViewController *_self = self;

        self.apiClient.detailSearchFinishedCallBack = ^(DetailModel  *detailModel)
        {
              [_self.loadingView stopAnimating];
        };
    }

    return _apiClient;
}

也许您可以考虑使用由计算属性处理的私有存储属性来进行更文字的 Swift 转换:

private var _apiClient: MyApiClient!

var apiClient: MyApiClient {
    if _apiClient == nil {
        _apiClient = MyApiClient()

        _apiClient.detailSearchFinishedCallBack = { [weak self] detailModel in
            if let loadingView = self?.loadingView {
                loadingView.stopAnimating()
            }
        }
    }

    return _apiClient
}

注意,这不是线程安全的(但您的 Objective-C 版本也不是),但它完成了您的要求。

顺便说一句,还有其他方法可以解决这个问题:

  • 您可以将初始化代码从这些闭包中移到其他一些逻辑共享初始化例程中,例如viewDidLoad 或者你有什么。这样一来,初始化事物的顺序就很明确了。

  • 您可以通过让 API 发布视图可以观察到的通知来进一步将 API 与 UI 分离。

【讨论】:

  • 惰性属性是特殊情况:你可以在初始化闭包中使用 self ,因为在对象完全初始化之前无法访问惰性属性
  • @Silmaril - 同意。在我最初写这个答案的时候,编译器对那个语法感到窒息,因此是丑陋的解决方法。但是您现在可以使用 OP 在他的问题中提出的语法。
【解决方案2】:

我不确定你无法做什么。

实际上,Xcode 在lazy var x:Type = { ... }() 模式中使用self 存在一些问题。例如,它不会自动完成属性或方法,但它可以工作。

此代码在 Xcode 6.3 Beta2 中编译和工作:

class LoadingView: UIView {}
class DetailModel {}

class MyApiClient {
    var detailSearchFinishedCallBack: ((DetailModel!) -> Void)?
}

class MyViewController1: UIViewController {

    override func viewDidLoad() {
        self.apiClient.detailSearchFinishedCallBack?(DetailModel())
    }

    lazy var apiClient: MyApiClient = {
        let apiClient: MyApiClient = MyApiClient()
        apiClient.detailSearchFinishedCallBack = { [weak self] (detailModel: DetailModel!) in

            println(self?.loadingView.frame)

            return
        }
        return apiClient
    }()

    lazy var loadingView: LoadingView = {
        let loadingView: LoadingView = LoadingView(frame: CGRectMake(0, 0, 200, 200))
        loadingView.center = self.view.center
        return loadingView
    }()
}

对于 Xcode 6.1.1,由于某些原因,编译器会崩溃。看来,我们必须在闭包之外至少引用一次self

        self // <- reference `self` in anyway

        apiClient.detailSearchFinishedCallBack = { [weak self] (detailModel: DetailModel!) in
            println(_self?.loadingView.frame)
            return
        }

最后,如果你觉得有些不安全,你可以直接做相当于 Objective-C 的:

class MyViewController2: UIViewController {

    private var _apiClient: MyApiClient? = nil
    var apiClient: MyApiClient {
        if _apiClient == nil {
            let apiClient : MyApiClient = MyApiClient()
            apiClient.detailSearchFinishedCallBack = { [weak self] (detailModel : DetailModel!) in
                println(self?.loadingView.frame)
                return
            }
            _apiClient = apiClient
        }
        return _apiClient!
    }

    private var _loadingView: LoadingView? = nil
    var loadingView : LoadingView {
        if _loadingView == nil {
            let loadingView : LoadingView = LoadingView(frame: CGRectMake(0, 0, 200, 200))
            loadingView.center = self.view.center
            _loadingView = loadingView
        }
        return _loadingView!
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-28
    • 2014-09-28
    • 1970-01-01
    • 2016-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-22
    相关资源
    最近更新 更多