简体   繁体   English

自我参考迅速关闭

[英]self reference swift closure

I have a lazy property in swift that has a callback that looks like this. 我在swift中有一个懒惰的属性,它有一个看起来像这样的回调。

lazy var apiClient: MyApiClient =
    {

        var apiClient : MyApiClient = MyApiClient()

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

        }

        return apiClient
    }()

I have another lazy load property that I would like to access inside the closure that looks like this: 我有另一个懒惰的load属性,我想在闭包内访问,如下所示:

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

        return loadingView
    }()

However, I'm unable to reference the loading view inside the closure. 但是,我无法引用闭包内的加载视图。 In objective c, this would look something like this. 在目标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;
}

Would someone be kind enough to show me the equivalent of this in Swift? 有人能够在Swift中向我展示相同的内容吗?

UPDATE: 更新:

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

                println(self?.loadingView.frame)

                return
            }
            return apiClient
            }()

So I went ahead and tried implementing the proposed solution but I get a compilation error. 所以我继续尝试实现提议的解决方案,但是我收到了编译错误。 Specifically I'm running into this error: 具体来说,我遇到了这个错误:

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

I'm not sure if this is something to do with my swift configuration in my project or not but I'm using the bridging header to import my objective c header files to be exposed to Swift. 我不确定这是否与我的项目中的swift配置有关,但我正在使用桥接头来导入我的目标c头文件以暴露给Swift。 I can't think of anything else that could be causing this but any help would be appreciated to resolve this. 我无法想到任何其他可能导致这种情况的事情,但任何帮助都将得到解决。

The syntax would be: 语法是:

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

At the time I originally answered this question, the Swift compiler had problem interpreting the above syntax, so I proposed the following ugly workaround outlined below. 当我最初回答这个问题时,Swift编译器在解释上述语法时遇到了问题,因此我提出了下面概述的以下丑陋的解决方法。 But you can now use the above simple, logical syntax. 但您现在可以使用上述简单的逻辑语法。


Original answer: 原始答案:

You are using a closure to provide a default value for a property. 您正在使用闭包为属性提供默认值。 But in the Setting a Default Property Value with a Closure or Function section of the Initialization Chapter of The Swift Programming Language says: 但是在Swift编程语言初始化章节中使用Closure或Function设置默认属性值部分说:

Note: If you use a closure to initialize a property, remember that the rest of the instance has not yet been initialized at the point that the closure is executed. 注意:如果使用闭包来初始化属性,请记住在执行闭包时尚未初始化实例的其余部分。 This means that you cannot access any other property values from within your closure, even if those properties have default values. 这意味着您无法从闭包中访问任何其他属性值,即使这些属性具有默认值也是如此。 You also cannot use the implicit self property, or call any of the instance's methods. 您也不能使用隐式self属性,也不能调用任何实例的方法。

Having said that, one could make an argument that the compiler should make allowances for lazy variables, but it doesn't. 话虽如此,人们可以提出一个论点,即编译器应该允许惰性变量,但事实并非如此。


You say you want an implementation of this Objective-C code: 您说您想要实现此Objective-C代码:

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

        __weak FeedViewController *_self = self;

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

    return _apiClient;
}

Perhaps you could consider a more literal Swift conversion, using private stored property which is handled by a computed property: 也许您可以考虑使用由计算属性处理的私有存储属性的更直接的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
}

Note, this is not thread safe (but your Objective-C rendition isn't either), but it accomplishes what you asked for. 注意,这不是线程安全的(但是你的Objective-C再现也不是),但它完成了你的要求。

As an aside there are other approaches to solve this: 除此之外,还有其他方法可以解决这个问题:

  • You could move the initialization code from these closures and into some other logical shared initialization routine, eg viewDidLoad or what have you. 您可以将初始化代码从这些闭包中移动到其他逻辑共享初始化例程中,例如viewDidLoad或者您有什么。 This way, the order in which things are initialized is explicit and unambiguous. 这样,事物初始化的顺序是明确且明确的。

  • You could further decouple the API from the UI by having the API post notifications that the view could observe. 您可以通过让API发布视图可以观察到的通知来进一步将API与UI分离。

I'm not sure what you unable to do. 我不确定你无法做什么。

Actually, Xcode has some problem about using self in lazy var x:Type = { ... }() pattern. 实际上,Xcode在lazy var x:Type = { ... }()使用self有一些问题lazy var x:Type = { ... }()模式。 For example, it doesn't auto-complete the properties or methods, but it works. 例如,它不会自动完成属性或方法,但它可以工作。

This code compiles and works in Xcode 6.3 Beta2: 此代码在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
    }()
}

As for Xcode 6.1.1, for some reasons, the compiler crashes. 至于Xcode 6.1.1,由于某些原因,编译器崩溃了。 It seems, we have to reference the self at least once outside of the closure. 看来,我们必须在封闭之外至少引用一次self

        self // <- reference `self` in anyway

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

Finally, if you feel something unsafe, you can do straight-forward equivalent to Objective-C: 最后,如果您觉得不安全,可以直接进行与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!
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM