我想知道Swift对象的一组属性何时更改。以前,我已经在Objective-C中实现了此功能,但是在将其转换为Swift时遇到了一些困难。
我之前的Objective-C代码是:
- (void) observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context {
if (![change[@"new"] isEqual:change[@"old"]])
[self edit];
}
我对Swift解决方案的第一遍是:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if change?[.newKey] != change?[.oldKey] { // Compiler: "Binary operator '!=' cannot be applied to two 'Any?' operands"
edit()
}
}
但是,编译器抱怨:“二进制运算符’!=’不能应用于两个’Any?’。操作数”
我的第二次尝试:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let newValue = change?[.newKey] as? NSObject {
if let oldValue = change?[.oldKey] as? NSObject {
if !newValue.isEqual(oldValue) {
edit()
}
}
}
}
但是,考虑到这一点,我认为这不适用于诸如Int之类的快速对象的原语(我假设它不是从NSObject继承的),而与Objective-
C版本不同的是,放置在其中时,Objective-C版本不会装箱在NSNumber中更改字典。
因此,问题是我该如何执行看似简单的任务来确定在Swift3中使用KVO是否实际更改了值?
另外,额外的问题是,如何利用“对象”变量?它不会让我更改名称,当然也不喜欢其中带有空格的变量。
以下是我最初的Swift 3答案,但是Swift 4简化了过程,无需进行任何强制转换。例如,如果你正在观察的Int
属性调用bar
的的foo
对象:
class Foo: NSObject {
@objc dynamic var bar: Int = 42
}
class ViewController: UIViewController {
let foo = Foo()
var token: NSKeyValueObservation?
override func viewDidLoad() {
super.viewDidLoad()
token = foo.observe(\.bar, options: [.new, .old]) { [weak self] object, change in
if change.oldValue != change.newValue {
self?.edit()
}
}
}
func edit() { ... }
}
注意,这种基于封闭的方法:
使您无需实施单独的observeValue
方法;
消除了指定context
和检查上下文的需要;和
该change.newValue
和change.oldValue
类型正确,省去了人工铸塑的需要。如果该属性是可选的,则可能必须安全地对其进行包装,但是不需要强制转换。
您唯一需要注意的是确保您的闭包不会引入强大的参考周期(因此使用[weak self]
模式)。
我最初的Swift 3答案如下。
你说:
但是,考虑到这一点,我认为这不适用于迅速对象的原语,例如
Int
(我认为)它们不继承自,NSObject
并且与将Objective-
C版本NSNumber
放入更改字典时不会装箱一样。
实际上,如果您查看这些值,则如果观察到的属性是an Int
,则它确实会作为a通过字典NSNumber
。
因此,您可以留在NSObject
世界上:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let newValue = change?[.newKey] as? NSObject,
let oldValue = change?[.oldKey] as? NSObject,
!newValue.isEqual(oldValue) {
edit()
}
}
或将它们用作NSNumber
:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let newValue = change?[.newKey] as? NSNumber,
let oldValue = change?[.oldKey] as? NSNumber,
newValue.intValue != oldValue.intValue {
edit()
}
}
或者,如果这是某些Swift类Int
的某些dynamic
属性的值,我将其强制转换为Int
:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let newValue = change?[.newKey] as? Int, let oldValue = change?[.oldKey] as? Int, newValue != oldValue {
edit()
}
}
你问:
另外,奖金问题,如何使用
of object
变量?它不会让我更改名称,当然也不喜欢其中带有空格的变量。
的of
是该参数的外部标签(如果您是调用此方法;在这种情况下,OS调用这个对我们来说,这样我们就不会在方法签名中使用这个外部的标签短)。的object
是内部标记物(该方法本身内使用)。Swift有一段时间可以使用外部和内部参数标签,但是从Swift
3开始,它才真正包含在API中。
就何时使用此change
参数而言,如果要观察多个对象的属性,并且这些对象在KVO上需要不同的处理,则可以使用它,例如:
foo.addObserver(self, forKeyPath: #keyPath(Foo.bar), options: [.new, .old], context: &observerContext)
baz.addObserver(self, forKeyPath: #keyPath(Foo.qux), options: [.new, .old], context: &observerContext)
然后:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard context == &observerContext else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
if (object as? Foo) == foo {
// handle `foo` related notifications here
}
if (object as? Baz) == baz {
// handle `baz` related notifications here
}
}
顺便说context
一句,我通常建议使用,例如,使用private var
:
private var observerContext = 0
然后使用该上下文添加观察者:
foo.addObserver(self, forKeyPath: #keyPath(Foo.bar), options: [.new, .old], context: &observerContext)
然后observeValue
确保它是它的context
,而不是它的超类建立的:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard context == &observerContext else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
if let newValue = change?[.newKey] as? Int, let oldValue = change?[.oldKey] as? Int, newValue != oldValue {
edit()
}
}
问题内容: 我想知道是否有一种方法可以检查Redis列表中是否已存在密钥? 我不能使用集合,因为我不想强制唯一性,但是我确实希望能够检查字符串是否确实存在。 问题答案: 您的选择如下: 如果发现并使用并替换它。 与您的个人保持独立 循环浏览直到找到项目或到达末尾。 Redis列表是作为http://en.wikipedia.org/wiki/Linked_list实现的,因此存在局限性。 我认为您
问题内容: 如果是这样,那么在Objective-C中使用键值观察时是否没有其他键差异? 问题答案: (编辑以添加新信息):考虑使用Combine框架是否可以帮助您完成所需的工作,而不是使用KVO 是的,没有。KVO一直以来都在处理NSObject子类。它不适用于不继承NSObject的类。斯威夫特(目前至少)没有自己的原生观察系统。 (有关如何将其他属性公开为ObjC的信息,请参见注释,以便KV
一个类似于Iterator中的的方法,在异常抛出和布尔返回行为中(尽管没有与的约定)。 示例: 目标是在客户端代码调用此方法而不消耗流的情况下早期失败。 一个可接受的答案也可以是“没有解决方案存在”,并有充分的理由说明为什么规范不能添加这样的方法(如果有充分的理由的话)。看起来JDK流通常在其终端方法的开头有以下代码段: 因此,对于这些流,实现这样的方法似乎并不困难。
问题内容: Mysql,检查字段值是否更改?如果更改值,则显示不同的值 table:一个 在此查询数据之类的。 大小更改时,我需要更改索引。我想获取此类数据。喜欢。 问题答案: 这应该可以解决问题: 输出: 在这里摆弄。
我需要验证表中是否已经存在列。我的类扩展了CustomTaskChange,因此我的方法接收一个数据库对象作为参数。我可以通过ResultSetObject进行我想要的验证吗?
问题内容: 我有一个Python字典列表,如下所示: 我想检查列表中是否已存在具有特定键/值的字典,如下所示: 问题答案: 这是一种实现方法: 括号中的部分是一个生成器表达式,该表达式将为每个具有要查找的键值对的字典返回,否则为。 如果密钥也可能丢失,则上面的代码可以给您一个。您可以通过使用并提供默认值来解决此问题。如果不提供 默认 值,则返回。