Skip to content

Instantly share code, notes, and snippets.

@norio-nomura
Last active August 29, 2015 14:18
Show Gist options
  • Save norio-nomura/b6c2ad88af55fea1f812 to your computer and use it in GitHub Desktop.
Save norio-nomura/b6c2ad88af55fea1f812 to your computer and use it in GitHub Desktop.
Swiftで、NSObjectのサブクラスを定義するスコープとdynamicの有無によるKVC/KVO挙動の違い

NSObjectのサブクラスを定義するスコープとdynamicの有無によるKVC/KVO挙動の違い

  • ファイルスコープ
    • dynamicなし、KVC使える、KVO使えない。
    • dynamicあり、KVC/KVO使える。
  • funcスコープ
    • dynamicなし、KVC/KVO使えない。
    • dynamicあり、KVC/KVO使える。

ファイルスコープはNSObjectではなく、そのサブクラスをサブクラス化してるような挙動。

SwiftでKVC/KVOを利用した場合の挙動。 Objective-CからSwiftのクラスを利用する場合は、ファイルスコープしか使えずdynamicの有無に直接的な影響は受けない。

import Foundation
import XCTest
class testTests: XCTestCase {
func testKVCandKVO_ClassInsideFuncNonDynamicProperty() {
println("\n==== \(__FUNCTION__)")
@objc(ClassInsideFuncNonDynamicProperty) class ClassInsideFunc: NSObject {
var nonDynamicProperty = 0
dynamic var dynamicProperty = 0
override func valueForUndefinedKey(key: String) -> AnyObject? {
println("\(key) is undefined on \(self)")
return nil
}
}
let propertyName = "nonDynamicProperty"
var e = ClassInsideFunc()
e.addObserver(self, forKeyPath: propertyName, options: .Initial | .New, context: nil)
e.nonDynamicProperty = 2
e.removeObserver(self, forKeyPath: propertyName)
var value = e.valueForKey(propertyName)
println("before setValue: \(value)")
e.setValue(0, forKey: propertyName) // NSUnknownKeyException
value = e.valueForKey(propertyName)
println("after setValue: \(value)")
}
func testKVCandKVO_ClassInsideFuncDynamicProperty() {
println("\n==== \(__FUNCTION__)")
class ClassInsideFunc: NSObject {
dynamic var dynamicProperty = 0
override func valueForUndefinedKey(key: String) -> AnyObject? {
println("\(key) is undefined on \(self)")
return nil
}
}
let propertyName = "dynamicProperty"
var e = ClassInsideFunc()
e.addObserver(self, forKeyPath: propertyName, options: .Initial | .New, context: nil)
e.dynamicProperty = 2
e.removeObserver(self, forKeyPath: propertyName)
var value = e.valueForKey(propertyName)
println("before setValue: \(value)")
e.setValue(0, forKey: propertyName)
value = e.valueForKey(propertyName)
println("after setValue: \(value)")
}
func testKVCandKVO_ClassOutsideFuncNonDynamicProperty() {
println("\n==== \(__FUNCTION__)")
let propertyName = "nonDynamicProperty"
var e = ClassOutsideFunc()
e.addObserver(self, forKeyPath: propertyName, options: .Initial | .New, context: nil)
e.nonDynamicProperty = 2
e.removeObserver(self, forKeyPath: propertyName)
var value = e.valueForKey(propertyName)
println("before setValue: \(value)")
e.setValue(0, forKey: propertyName)
value = e.valueForKey(propertyName)
println("after setValue: \(value)")
}
func testKVCandKVO_ClassOutsideFuncDynamicProperty() {
println("\n==== \(__FUNCTION__)")
let propertyName = "dynamicProperty"
var e = ClassOutsideFunc()
e.addObserver(self, forKeyPath: propertyName, options: .Initial | .New, context: nil)
e.dynamicProperty = 2
e.removeObserver(self, forKeyPath: propertyName)
var value = e.valueForKey(propertyName)
println("before setValue: \(value)")
e.setValue(0, forKey: propertyName)
value = e.valueForKey(propertyName)
println("after setValue: \(value)")
}
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
println("\(keyPath) = \(change)")
}
}
@objc class ClassOutsideFunc: NSObject {
var nonDynamicProperty = 0
dynamic var dynamicProperty = 0
override func valueForUndefinedKey(key: String) -> AnyObject? {
println("\(key) is undefined on \(self)")
return nil
}
}
Test Suite 'All tests' started at 2015-04-07 07:14:16 +0000
Test Suite 'testTests.xctest' started at 2015-04-07 07:14:16 +0000
Test Suite 'testTests' started at 2015-04-07 07:14:16 +0000
Test Case '-[testTests.testTests testKVCandKVO_ClassInsideFuncDynamicProperty]' started.
==== testKVCandKVO_ClassInsideFuncDynamicProperty()
dynamicProperty = [kind: 1, new: 0]
dynamicProperty = [kind: 1, new: 2]
before setValue: Optional(2)
after setValue: Optional(0)
Test Case '-[testTests.testTests testKVCandKVO_ClassInsideFuncDynamicProperty]' passed (0.001 seconds).
Test Case '-[testTests.testTests testKVCandKVO_ClassInsideFuncNonDynamicProperty]' started.
==== testKVCandKVO_ClassInsideFuncNonDynamicProperty()
nonDynamicProperty is undefined on <ClassInsideFuncNonDynamicProperty: 0x10041d260>
nonDynamicProperty = [kind: 1, new: <null>]
nonDynamicProperty is undefined on <ClassInsideFuncNonDynamicProperty: 0x10041d260>
before setValue: nil
<unknown>:0: error: -[testTests.testTests testKVCandKVO_ClassInsideFuncNonDynamicProperty] : failed: caught "NSUnknownKeyException", "[<ClassInsideFuncNonDynamicProperty 0x10041d260> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key nonDynamicProperty."
(
0 CoreFoundation 0x00007fff8ee9503c __exceptionPreprocess + 172
1 libobjc.A.dylib 0x00007fff98f4076e objc_exception_throw + 43
2 CoreFoundation 0x00007fff8ee94bd9 -[NSException raise] + 9
3 Foundation 0x00007fff94294e7d -[NSObject(NSKeyValueCoding) setValue:forKey:] + 395
4 testTests 0x00000001000b45d7 _TTSf4g___TFC9testTests9testTests47testKVCandKVO_ClassInsideFuncNonDynamicPropertyfS0_FT_T_ + 727
5 testTests 0x00000001000b3346 _TToFC9testTests9testTests47testKVCandKVO_ClassInsideFuncNonDynamicPropertyfS0_FT_T_ + 22
6 CoreFoundation 0x00007fff8ed6c7bc __invoking___ + 140
7 CoreFoundation 0x00007fff8ed6c612 -[NSInvocation invoke] + 290
8 XCTest 0x000000010071399c -[XCTestCase invokeTest] + 253
9 XCTest 0x0000000100713b9d -[XCTestCase performTest:] + 150
10 XCTest 0x000000010071c6e0 -[XCTest run] + 257
11 XCTest 0x00000001007128ab -[XCTestSuite performTest:] + 379
12 XCTest 0x000000010071c6e0 -[XCTest run] + 257
13 XCTest 0x00000001007128ab -[XCTestSuite performTest:] + 379
14 XCTest 0x000000010071c6e0 -[XCTest run] + 257
15 XCTest 0x00000001007128ab -[XCTestSuite performTest:] + 379
16 XCTest 0x000000010071c6e0 -[XCTest run] + 257
17 XCTest 0x000000010070f8a9 __25-[XCTestDriver _runSuite]_block_invoke + 56
18 XCTest 0x000000010071b34d -[XCTestObservationCenter _observeTestExecutionForBlock:] + 162
19 XCTest 0x000000010070f7e0 -[XCTestDriver _runSuite] + 269
20 XCTest 0x00000001007103c9 -[XCTestDriver _checkForTestManager] + 678
21 XCTest 0x000000010071f580 +[XCTestProbe runTests:] + 182
22 xctest 0x0000000100001266 xctest + 4710
23 xctest 0x00000001000015e2 xctest + 5602
24 xctest 0x0000000100000ee3 xctest + 3811
25 libdyld.dylib 0x00007fff906925c9 start + 1
26 ??? 0x000000000000000a 0x0 + 10
)
Test Case '-[testTests.testTests testKVCandKVO_ClassInsideFuncNonDynamicProperty]' failed (0.070 seconds).
Test Case '-[testTests.testTests testKVCandKVO_ClassOutsideFuncDynamicProperty]' started.
==== testKVCandKVO_ClassOutsideFuncDynamicProperty()
dynamicProperty = [kind: 1, new: 0]
dynamicProperty = [kind: 1, new: 2]
before setValue: Optional(2)
after setValue: Optional(0)
Test Case '-[testTests.testTests testKVCandKVO_ClassOutsideFuncDynamicProperty]' passed (0.001 seconds).
Test Case '-[testTests.testTests testKVCandKVO_ClassOutsideFuncNonDynamicProperty]' started.
==== testKVCandKVO_ClassOutsideFuncNonDynamicProperty()
nonDynamicProperty = [kind: 1, new: 0]
before setValue: Optional(2)
after setValue: Optional(0)
Test Case '-[testTests.testTests testKVCandKVO_ClassOutsideFuncNonDynamicProperty]' passed (0.000 seconds).
Test Suite 'testTests' failed at 2015-04-07 07:14:16 +0000.
Executed 4 tests, with 1 failure (1 unexpected) in 0.073 (0.074) seconds
Test Suite 'testTests.xctest' failed at 2015-04-07 07:14:16 +0000.
Executed 4 tests, with 1 failure (1 unexpected) in 0.073 (0.075) seconds
Test Suite 'All tests' failed at 2015-04-07 07:14:16 +0000.
Executed 4 tests, with 1 failure (1 unexpected) in 0.073 (0.076) seconds
@norio-nomura
Copy link
Author

Objective-Cから利用可能なSwiftのクラスは、ファイルスコープ定義のものだけなので、Objective-C利用を前提としたKVC/KVOの互換コードは、ファイルスコープ定義のものにしか使われていないと想像。
Objective-CからSwiftのクラスを利用する際には、dynamicの有無は関係ない。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment