无法在Swift中使用自定义setter将nil设置为可选属性

时间:2016-01-07 12:48:04

标签: swift

我有各种属性的swift类,其中一些属性具有可选类型。

class UserObject: PFUser{

   //optional property
   var optionalPhotoURL:String? {
       get {
           if let optionalPhotoURL:String = objectForKey("optionalPhotoURL"){
               return optionalPhotoURL
           }
           //not needed but just for emphasis
           return nil
       }

       //I am unable to set UserObject.optionalPhotoURL = nil with this setters
       set {
          if let newPhotoURL:String = newValue! {
              setObject(newPhotoURL, forKey: "optionalPhotoURL")
           }else{
             self.optionalPhotoURL = nil
           }
       }
   } 
}

我无法将optionalPhotoURL设置为nil,如果它已经为其分配了先前的值。

我想我的问题是,如何使用自定义setter“取消设置”可选属性?

更新

这些安装程序全部崩溃

   set {
      if let newPhotoURL:String = newValue {
          setObject(newPhotoURL, forKey: "optionalPhotoURL")
       }else{
         self.optionalPhotoURL = nil
       }
   }

和这个

    set {
        if (newValue != nil) {
            setObject(newValue!, forKey: "optionalPhotoURL")
        }else{
            self.optionalPhotoURL = nil
        }
    }

3 个答案:

答案 0 :(得分:3)

这里有一个计算属性。

可以计算或存储Swift属性。我们可以使用didSetwillSet观察我们存储的属性中的值更改,但此处我们仍然有一个存储属性。

在您的情况下,由于您已覆盖setget * ,因此您没有存储属性,而是具有计算属性。如果您希望计算属性具有后备存储,则必须单独创建它。

所以你可能想要这样的东西:

class FooClass {
    private var storageProperty: String?
    var accessProperty: String? {
        get {
            return self.storageProperty
        }
        set {
            self.storageProperty = newValue
            // or whatever logic you may like here
        }
    }
} 

* :如果不覆盖set,则无法覆盖get。但是,您可以覆盖get而不覆盖set - 这会生成一个只读计算值。

此外,以依赖键值编码的方式实现存储属性非常重要。

对于初学者来说,setObject(forKey:)方法甚至不适用于纯Swift类型。这仅适用于从Objective-C类型继承的对象。这是从NSObject符合NSKeyValueCoding协议的继承方法。为什么Objective-C的基础对象符合如此多的协议是超出我的...但是确实如此,我们无能为力。

如果我们有一个代码库,其中一些对象继承自Objective-C对象(基本上任何项目都有,UIViewController等),而我们的一些对象是纯Swift对象(其中)当你从头开始创建自己的Swift类时,你会倾向于拥有,然后我们的Swift对象将无法实现相同的模式。如果我们有两种类型的对象,我们要么必须实现我上面显示的所有模式(然后我们都有一致性),要么我们必须为某些类型实现一种模式而为其他类型实现另一种模式( Swift结构肯定要实现上面的模式,你不能只让它们继承自NSObject)(然后我们就会有不一致,我们不喜欢)。

setObject(forKey:)更糟糕的是,此方法的第一个参数始终为AnyObject类型。该方法根本没有类型安全性。通过setObject(forKey:)存储内容的地方完全基于我们使用的密钥。当我们使用setObject(forKey:)时,我们会获得Swift给我们带来的一堆类型安全优势,并将它们抛到窗外。如果我们不关心Swift为Objective-C提供的优势,我们为什么要在Swift中编写呢?

当我们使用setObject(forKey:)时,我们甚至无法使存储的属性私有。知道密钥的任何人都可以调用setObject(forKey:)并将该对象设置为他们想要的任何内容。这包括不是字符串的对象。我们要为此代码库引入崩溃所需要做的就是编写一个类扩展或子类,它与您使用的不同类型的密钥冲突(因此可能是同一密钥的NSData值)。即使它不是一个不同的类型,只需复制多个属性的相同密钥就会引入错误......以及难以调试的错误。

答案 1 :(得分:2)

永远不要通过调用自己在set范围内设置计算属性的值! 这会导致无限循环,应用程序将崩溃。

我不知道哪个API setObject:forKey属于,但在nil的情况下,您应该删除该对象

set {
    if let newPhotoURL = newValue {
       setObject(newPhotoURL, forKey: "optionalPhotoURL")
    } else {
       removeObjectForKey("optionalPhotoURL")
    }
 }

答案 2 :(得分:0)

您的属性optionalPhotoURL是计算属性,它不存储任何值:
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html
您可能想要创建一个实际存储该值的附加属性。但是,为什么要将它设置为nil,因为如果没有nil,你不会删除它们的对象。