【问题标题】:Set value of read-only stored property during initializing in Swift在 Swift 中初始化期间设置只读存储属性的值
【发布时间】:2014-07-30 14:29:09
【问题描述】:

我想实现我的自定义 MKAnnotation。我看了一下 MKAnnotation 协议(MKAnnotation.h)。 如下:

//
//  MKAnnotation.h
//  MapKit
//
//  Copyright (c) 2009-2014, Apple Inc. All rights reserved.
//

protocol MKAnnotation : NSObjectProtocol {

    // Center latitude and longitude of the annotation view.
    // The implementation of this property must be KVO compliant.
    var coordinate: CLLocationCoordinate2D { get }

    // Title and subtitle for use by selection UI.
    @optional var title: String! { get }
    @optional var subtitle: String! { get }

    // Called as a result of dragging an annotation view.
    @optional func setCoordinate(newCoordinate: CLLocationCoordinate2D)
}

请注意坐标属性(这是一个只读存储属性)。 以下是我实现此协议的方式:

class RWDefaultPin: NSObject, MKAnnotation {
    var title:String = ""
    var subtitle:String = ""
    var groupTag:String = ""
    var coordinate: CLLocationCoordinate2D { get {
        return self.coordinate // this is obviously wrong because property's trying to return itself
    } };


    init(coordinate:CLLocationCoordinate2D) {
        super.init()
        self.coordinate = coordinate
    }
}

但显然编译器抱怨我的 init 方法,我试图分配给我的坐标属性 Cannot assign to 'coordinate' in 'self' 显然是因为它是一个只读属性。

以前在 Objective-C 中,我们可以克服这个问题,因为属性由 ivars 支持。

我希望 Swift 中有访问修饰符,这样我就可以在我的类中定义一个私有属性并在 init 上设置它的值,并在 get 坐标操作时返回它的值,但是没有这样的东西!

我不知道如何在 Swift 中解决此问题,或者我需要将其完全打开并将我的坐标更改为可读/可写?

【问题讨论】:

  • 您是否尝试创建一个真正私有的coordinate 属性?一个可以在初始化时设置,是公共coordinategetter返回的值?
  • 为什么不给它添加一个set
  • @Firo,这个属性曾经在 Objective-C 中是只读的,如果你注意到,苹果在 MKAnnotation.h 中将它定义为只读(变量坐标:CLLocationCoordinate2D { get })
  • @CjCoax 并不意味着你的班级可以给它一个二传手。只是意味着协议不会强制执行 setter,它会阻止不知道实际类的任何人设置属性。
  • @CjCoax,我犯了一个错误。显然你不能按照我的建议去做。在 setter 中设置属性实际上会陷入无限循环(因为现在已设置,所以它会再次调用 setter)。对不起。见编辑。

标签: swift


【解决方案1】:

即使协议中的属性是{ get },这也只是建立了一个最低标准。将其定义为读写是完全可以接受的:

class MyAnnotation:NSObject, MKAnnotation
{
    var coordinate:CLLocationCoordinate2D

    init(coordinate:CLLocationCoordinate2D) {
        self.coordinate = coordinate
    }
}

或者,如果你真的想保持只读,你可以使用 let:

class MyAnnotation:NSObject, MKAnnotation
{
    let coordinate:CLLocationCoordinate2D

    init(coordinate:CLLocationCoordinate2D) {
        self.coordinate = coordinate
    }
}

【讨论】:

    【解决方案2】:

    您应该能够只添加一个setter 并将信息存储在一个内部坐标值中。由于您有一个吸气剂,它仍然符合协议:

    var innerCoordinate: CLLocationCoordinate2D
    
    var coordinate: CLLocationCoordinate2D { 
        get {
            return self.innerCoordinate
        } 
        set {
            self.innerCoordinate = newValue
        }
    };
    
    init(coordinate:CLLocationCoordinate2D) {
        super.init()
        self.innerCoordinate = coordinate
    }
    

    这实际上是我实现只读和私有属性的方式(使用协议和工厂模式)。我使用公共接口设置协议,使用私有变量和设置器设置类。它实际上是设置代码的超级干净的方式(并且解决了 Swift 中缺少受保护/私有属性的问题)。


    这是我正在谈论的内容的一个抽象示例(如果您关心的话):

    // this is your MKAnnotation in this example
    protocol SomeProtocol {
        var getterProperty: String { get }
        var setterProperty: String { set get }
    
        func publicFunction(someStirng: String) -> ();
    
    }
    
    // setup a function that returns a class conforming to your needed protocol
    func SomeClassMaker() -> SomeProtocol {
        // your internal class that no one can access unless by calling the maker function
        class SomeClassInternal: NSObject, SomeProtocol {
    
            // private and no one can get to me!
            var innerSetterProperty = "default setter";
    
            var getterProperty = "default getter"
    
            var setterProperty: String {
                get {
                    return self.innerSetterProperty;
                }
                set {
                    "hit"
                    self.innerSetterProperty = newValue
                }
            }
    
            func publicFunction(someString: String) -> ()  {
                // anyone get me
                self.getterProperty = someString;
            }
    
            func privateFunction() -> () {
                // no one can get me except internal functions
            }
    
        }
    
        return SomeClassInternal();
    }
    
    
    // create the class
    var classInstance = SomeClassMaker();
    
    // totally fine!
    classInstance.setterProperty = "secret string"
    // prints "secret string"
    classInstance.setterProperty;
    
    // error! no public setter for "getter"
    classInstance.getterProperty = "another secret"
    
    classInstance.publicFunction("try secret again")
    // prints "try secret again"
    let blahed = classInstance.getterProperty
    
    // error!
    classInstance.privateFunction()
    

    【讨论】:

    • @JamieForrest 如果您可以访问该类,则可以这样做,但是如果您通过协议访问设计代码,那么访问器类只能获取它。这是拥有私有变量和函数的好方法。它还允许进行一些干净的单元测试。让我用一个完整的例子来完善我的答案......给我一秒钟
    • @JamieForrest,我用我试图解释的简短示例编辑了答案。我在原来的答案中也犯了一个错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-11
    • 1970-01-01
    • 2021-01-22
    • 1970-01-01
    • 1970-01-01
    • 2015-04-08
    • 1970-01-01
    相关资源
    最近更新 更多