今天在与Swift交往时,我遇到了一件奇怪的事情。这是我开发的单元测试,显示了使用Swift的AnyObject时的一些意外行为。
class SwiftLanguageTests: XCTestCase {
class TestClass {
var name:String?
var xyz:String?
}
func testAccessingPropertiesOfAnyObjectInstancesReturnsNils() {
let instance = TestClass()
instance.xyz = "xyz"
instance.name = "name"
let typeAnyObject = instance as AnyObject
// Correct: Won't compile because 'xyz' is an unknown property in any class.
XCTAssertEqual("xyz", typeAnyObject.xyz)
// Unexpected: Will compile because 'name' is a property of NSException
// Strange: But returns a nil when accessed.
XCTAssertEqual("name", typeAnyObject.name)
}
}
这段代码简化了一些其他代码,其中只有一个Swift函数只能返回AnyObject
。
不出所料,在创建的实例之后TestClass
,将其强制转换为AnyObject
并设置另一个变量,xyz
由于AnyObject
没有这样的属性,因此无法编译该属性。
但令人惊讶的name
是,编译器接受了一个名为的属性,因为on上有该名称的属性NSException
。看起来,只要它在运行时的某个地方存在,Swift很乐意接受任何属性名称。
下一个意外行为以及引起所有这些后果的是,尝试访问该name
属性将返回nil。观察调试器中的各种变量,我可以看到它typeAnyObject
指向原始TestClass
实例,并且它的name
属性值为“
name”。
Swift在访问时不会引发错误,typeAnyObject.name
因此我希望它能够找到并返回“名称”。但是相反,我得到零。
如果有人可以阐明这里发生的事情,我将很感兴趣。
我主要担心的是,我希望Swift在访问上不存在的属性时抛出错误AnyObject
,或者找到并返回正确的值。目前都没有发生。
与Objective-C中类似,您可以在其中发送任意消息id
,可以AnyObject
在Swift
的实例上调用任意属性和方法。但是,细节有所不同,并且 在“将Swift与Cocoa和Objective-C一起使用”一书中记录了 与Objective-C
API的交互
。
Swift包含
AnyObject
代表某种对象的类型。这类似于Objective-
C的id
类型。Swift导入id
为AnyObject
,这使您可以在保持无类型对象灵活性的同时编写类型安全的Swift代码。
…
您可以调用任何Objective-C方法并访问AnyObject值上的任何属性,而无需转换为更特定的类类型。这包括与Objective-
C兼容的方法和标有@objc
属性的属性。
…当您在
AnyObject
类型的值上调用方法时,该方法调用的行为类似于隐式展开的可选。您可以使用与协议中的可选方法相同的可选链接语法,以选择性地调用上的方法AnyObject
。
这是一个例子:
func tryToGetTimeInterval(obj : AnyObject) {
let ti = obj.timeIntervalSinceReferenceDate // NSTimeInterval!
if let theTi = ti {
print(theTi)
} else {
print("does not respond to `timeIntervalSinceReferenceDate`")
}
}
tryToGetTimeInterval(NSDate(timeIntervalSinceReferenceDate: 1234))
// 1234.0
tryToGetTimeInterval(NSString(string: "abc"))
// does not respond to `timeIntervalSinceReferenceDate`
obj.timeIntervalSinceReferenceDate
是一个隐式展开的可选nil
对象,如果对象不具有该属性。
这是检查和调用 方法 的示例:
func tryToGetFirstCharacter(obj : AnyObject) {
let fc = obj.characterAtIndex // ((Int) -> unichar)!
if let theFc = fc {
print(theFc(0))
} else {
print("does not respond to `characterAtIndex`")
}
}
tryToGetFirstCharacter(NSDate(timeIntervalSinceReferenceDate: 1234))
// does not respond to `characterAtIndex`
tryToGetFirstCharacter(NSString(string: "abc"))
// 97
obj.characterAtIndex
是一个隐式展开的可选闭包。可以使用可选链接简化该代码:
func tryToGetFirstCharacter(obj : AnyObject) {
if let c = obj.characterAtIndex?(0) {
print(c)
} else {
print("does not respond to `characterAtIndex`")
}
}
就您而言,TestClass
没有任何@objc
属性。
let xyz = typeAnyObject.xyz // error: value of type 'AnyObject' has no member 'xyz'
无法编译,因为该xyz
属性对于编译器是未知的。
let name = typeAnyObject.name // String!
进行编译是因为-如您所注意到- NSException
具有name
属性。但是,该值是nil
因为TestClass
没有与
Objective-C兼容的 name
方法。如上所述,您应该使用可选绑定来安全地解包值(或针对进行测试nil
)。
如果您的班级来自 NSObject
class TestClass : NSObject {
var name : String?
var xyz : String?
}
然后
let xyz = typeAnyObject.xyz // String?!
会编译。(或者,用标记类或属性@objc
。)但是现在
let name = typeAnyObject.name // error: Ambigous use of `name`
不再编译。其原因是,这两个TestClass
和NSException
具有name
属性,但具有不同类型(String?
VS
String
),使表达式的类型是不明确的。这种歧义只能通过(可选)将其强制转换AnyObject
为TestClass
:
if let name = (typeAnyObject as? TestClass)?.name {
print(name)
}
结论:
AnyObject
是否与Objective-C兼容的情况下调用该方法/属性。nil
或使用可选绑定来检查实例是否确实具有该方法/属性。特别是由于最后一点,如果可能的话,我将尽量避免使用此机制,而是可选地将其强制转换为已知的类(如上例所示)。
我有以下代码来解析一个JSON文件: 要处理以下JSON文件: 如果我执行此代码,我将收到以下错误: 所以我开始一步一步地调试应用程序,看看part processing()中的哪个代码部分抛出了这个异常。令人惊讶的是,那里的所有代码都正常执行:没有抛出异常,也没有返回结果I except。 更让我惊讶的是,当我稍微改变第一种方法的代码时,它可以在不产生异常的情况下工作。 我不知道println方
问题内容: 我在GregorianCalendar类中遇到一个奇怪的行为,我想知道我是否真的做得不好。 仅当初始化日期的月份的实际Maximum大于我将日历设置为的月份时,才追加此值。 这是示例代码: 我知道问题是由于日历初始化日期是31天(可能是5月),与设置为2月(28天)的月份混淆了。修复很容易(只需在设置年和月之前将day_of_month设置为1),但是我想知道这确实是想要的行为。有什么
问题内容: 我正在为一个问题而苦苦挣扎,我不明白为什么它不起作用。如何通过将变量传递并转换为? 为什么在顶部代码段中不起作用,但在行下方的底部代码段中起作用? 唯一的区别似乎是添加了一个额外的变量,该变量也被键入为? 问题答案: 该是一种原始类型,同时是一个普通的Java类。您不能在原始类型上调用方法。但是该方法在上可用,如javadoc中所示 有关这些原始类型的更多信息,请参见此处
问题内容: 为什么的到哪里去了? 问题答案: 删除任何字符,并从字符串的开头和结尾。
问题内容: 我认为这是一个正常程序,但这是我得到的输出: 有人可以向我解释一下吗? 问题答案: 这是有据可查的PHP行为,请参阅php.net的foreach页面上的警告。 警告 即使在 foreach 循环之后,仍保留 $ value的 引用和最后一个数组元素。建议通过unset()销毁它。 __ 编辑 尝试逐步了解此处实际发生的情况
问题内容: 我有上面的代码,但我不知道为什么会产生 而不是 非常感谢 问题答案: 使用量词来匹配1个或多个空格,而不是:- 表示匹配0个或多个空格,并且将在每个字符之前匹配一个空字符,并由一个空格代替。