unrecognized selector sent to instance in swift

之前在用 swift 来写一个项目的时候遇到了这样的一个错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2015-03-17 17:25:55.249 MyProject[36665:3189330] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MyProject.Demo onRequestCompleted:]: unrecognized selector sent to instance 0x7f9fd3df1d00'
*** First throw call stack:
(
0 CoreFoundation 0x0000000102b36c65 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x0000000105097bb7 objc_exception_throw + 45
2 CoreFoundation 0x0000000102b3e0ad -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
3 CoreFoundation 0x0000000102a9413c ___forwarding___ + 988
4 CoreFoundation 0x0000000102a93cd8 _CF_forwarding_prep_0 + 120
5 CoreFoundation 0x0000000102b0654c __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
6 CoreFoundation 0x0000000102a04a04 _CFXNotificationPost + 2484
7 Foundation 0x00000001034ec968 -[NSNotificationCenter postNotificationName:object:userInfo:] + 66
...
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)

根据以往的 Objective-C经验来看的话,这里是因为传递了错误的参数导致 selector没有被识别。而且在 call stack 中我们可以看到 :

1
-[NSNotificationCenter postNotificationName:object:userInfo:]

以此推断,是因为我们在使用 NSNotificationCenteraddObserver 方法时,传入的 selector有错误,很可能是因为我们把方法名字写错了,但是经过检查之后发现我们的代码没有错误:

1
2
3
4
5
6
NSNotificationCenter.defaultCenter().addObserver(self, selector: "onRequestCompleted:", name: MY_NOTIFICATION_REQUESTCOMPLETED, object: context)
// the onRequestCompleted: method is in swift and like this:
private func onRequestCompleted(notification: NSNotification) -> Void{
// code here
}

百思不得其解只能求助文档和 Stackoverflow 了,因为这个错误太普遍,而且在 swift 中很多人还没有去踩坑。所以花了很长的时间才发现,原来在我们的文档中有这样的一段:

To be accessible and usable in Objective-C, a Swift class must be a descendant of an Objective-C class or it must be marked @objc.

Swift declarations marked with the private modifier do not appear in the generated header. Private declarations are not exposed to Objective-C unless they are explicitly marked with @IBAction,@IBOutlet, or @objc as well.

也就是说如果我们的 Swift 方法被 Objective-C所使用,那么这个 Swift类必须是继承自 Objecitve-C类,或者必须被标记成 @objc. 而 Swift中的 private 方法不会出现在生成的头文件中,所以这些私有的方法不会暴露给 Objective-C 除非被显示的标记为 @IBAction @Iboutlet或者@objc