keyPathsForValuesAffecting 与 NSManagedObject

keyPathsForValuesAffecting with NSManagedObject

您好, 我想将 Aaron 书中的 objective'c 练习翻译成 swift,但我找不到解决方案。 Objective'c 代码是:

@dynamic firstName;
@dynamic lastName;
@dynamic department;

+ (NSSet *)keyPathsForValuesAffectingFullName
{
    return [NSSet setWithObjects:@"firstName", @"lastName", nil];
}

- (NSString *)fullName
{
    NSString *first = [self firstName];
    NSString *last = [self lastName];
    if (!first)
        return last;
    if (!last)
        return first;
    return [NSString stringWithFormat:@"%@ %@", first, last];
}

我在开发者文档中找到了一个函数,但我不明白如何实现这段代码。

更明确地说,这是 Apple 文档

To-one Relationships

要自动触发一对一关系的通知,您应该重写 keyPathsForValuesAffectingValueForKey: 或实现一个合适的方法,该方法遵循它为注册相关键定义的模式。

例如,一个人的全名取决于名字和姓氏。 returns全名可以这样写的方法:

- (NSString *)fullName {
    return [NSString stringWithFormat:@"%@ %@",firstName, lastName];
}

观察 fullName 属性 的应用程序必须在 firstName 或 lastName 属性更改时得到通知,因为它们会影响 属性.

的值

一种解决方案是覆盖 keyPathsForValuesAffectingValueForKey:指定一个人的全名 属性 取决于 lastName 和 firstName 属性。清单 1 显示了此类依赖项的示例实现:

清单 1 keyPathsForValuesAffectingValueForKey 的示例实现:

+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {

    NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];

    if ([key isEqualToString:@"fullName"]) {
        NSArray *affectingKeys = @[@"lastName", @"firstName"];
        keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
    }
    return keyPaths;
}

class func keyPathsForValuesAffectingValueForKey(_ key: String) -> NSSet

谁能告诉我如何在 swift 中实现这个功能?

谢谢你帮助我。

如果您只想检查名称或部门何时更新,只需使用 Swift 的 dynamic 关键字,如下所示:

dynamic var firstName: String

然后您可以按照 Adopting Cocoa Design Patterns Guide

在 Swift 中遵循 Apple 关于采用键值观察的文档

总结:

  1. dynamic 关键字添加到您要观察的任何 属性。
  2. 根据 Apple 文档创建全局上下文变量。
  3. 覆盖 observeValueForKeyPath 方法。

我找到了问题的解决方案! 只需在

之前用 class 覆盖 func keyPathsForValuesAffectingValueForKey(key: String)

这里是代码:

class Locataires: NSManagedObject {
@NSManaged var firstName: String
@NSManaged var lastName: String
var fullName: NSString {
    get {
 return firstName + lastName
    }
}

override class func keyPathsForValuesAffectingValueForKey(key: String) -> NSSet {
    if key == «fullName « {
        let mesClefs = ["firstName", "lastName"]
        return NSSet(array: mesClefs)
    }
    else {
        return super.keyPathsForValuesAffectingValueForKey(key)
    }
}

感谢您的帮助 一月

对于Swift4的解决方案是:

@objc dynamic var firstName: String = ""
@objc dynamic var lastName: String = ""
@objc dynamic var fullName: String {
  return "\(firstName) \(lastName)"
}

@objc class func keyPathsForValuesAffectingFullName() -> Set<String> {
  return Set(["firstName", "lastName"])
}

此解决方案适用于 XCode 9+ 和 Swift 4.0(Apple Swift 版本 4.0.2(swiftlang-900.0. 69.2 铿锵声-900.0.38):

@objc public class MyKVOClass : NSObject
{
    // MARK: Properties

    @objc dynamic var myKVOSubclass : KVOSubclass?
    @objc dynamic var myKVOProperty : String?

    @objc dynamic var myComputedKVOProperty : String? {
        guard let mySubclassPropertyValue = self.myKVOSubclass?.mySubclassProperty,
            let myKVOPropertyValue = self.myKVOProperty else { return nil }

        let myComputedKVOPropertyValue = NSString(format:"%@ %@",mySubclassPropertyValue, myKVOPropertyValue)
        return myComputedKVOPropertyValue as String
    }

    // MARK: Initialization

    @objc public required override init() {
        super.init()
    }

  // MARK: KVO

  public override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String>
  {
    if key == "myComputedKVOProperty" {
      return Set(["myKVOSubclass",
      "myKVOSubclass.mySubclassProperty",
      "myKVOProperty"])
    }
    // NOTE : Add more keys with the name of the property if needed...

    return Set([])
  }

}

谢谢 Darkwonder。它适用于 Swift 4!!!

Before - 将 didSet 添加到所有依赖属性:

class MyViewController: NSViewController {

  // bound to a Value of NSTextField with NumberFormatter
  @objc dynamic var amountOfBetOnRed: Int = 0 {
    didSet {
      updatebetButtonEnabled()
    }
  }

  // same above
  @objc dynamic var amountOfBetOnBlack: Int = 0 {
    didSet {
      updatebetButtonEnabled()
    }
  }

  // bound to a Enabled of NSButton
  @objc dynamic var betButtonEnabled: Bool = false

  func updatebetButtonEnabled() {
    betButtonEnabled = amountOfBetOnRed != 0 || amountOfBetOnBlack != 0
  }

}

After - 用计算的 属性:

替换 didSet(s)
class MyViewController: NSViewController {

  @objc dynamic var amountOfBetOnRed: Int = 0

  @objc dynamic var amountOfBetOnBlack: Int = 0

  @objc dynamic var betButtonEnabled: Bool {
    get {
      return amountOfBetOnRed != 0 || amountOfBetOnBlack != 0
    }
  }

  override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
    print("Debug: called for:", key)

    switch key {
    case "betButtonEnabled" :
      return Set(["amountOfBetOnRed", "amountOfBetOnBlack"])
    default :
      return super.keyPathsForValuesAffectingValue(forKey: key)
    }
  }

}

get { }可以省略,只留下return ...,但是我这里用了get来强调它是一个计算出来的属性.

已通过 Xcode 9.0 与上述伪代码确认。

已添加:

到目前为止我学到了什么: 函数 keyPathsForValuesAffectingValue 在从 init() 返回后被调用了几次,每个 属性 名称被一个一个指定为 @objc dynamic。这个实现让我们知道哪些属性依赖于哪些属性。实际上,addObserver 是在幕后代表我们自动调用的。其他序数属性不会调用该函数,例如 var 而没有 @objc dynamic.

这里有一个更安全的(因为#keyPath),Swift 4 的更短版本:

override public class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
    return [
        #keyPath(Track.rTitle): [#keyPath(Track.title)],
        #keyPath(Track.rSource): [#keyPath(Track.album), #keyPath(Track.author)],
        ][key] ?? super.keyPathsForValuesAffectingValue(forKey: key)
}