首页 > 代码库 > 关于iOS中的kvo错误 _NSSetObjectValueAndNotify
关于iOS中的kvo错误 _NSSetObjectValueAndNotify
Foundation`_NSSetObjectValueAndNotify:
...
0x116156dfb <+27>: callq 0x116340498 ; symbol stub for: object_getClass
0x116156e00 <+32>: movq %rax, %rdi
0x116156e03 <+35>: callq 0x1163404a4 ; symbol stub for: object_getIndexedIvars
...
0x116156e12 <+50>: callq 0x116340636 ; symbol stub for: pthread_mutex_lock
...
0x116156e1e <+62>: callq 0x11633f20e ; symbol stub for: CFDictionaryGetValue
0x116156e23 <+67>: movq 0x31c35e(%rip), %rsi ; "copyWithZone:"
...
0x116156e2f <+79>: callq *0x27496b(%rip) ; (void *)0x00000001180cfac0: objc_msgSend
0x116156e35 <+85>: movq %rax, %r12
0x116156e38 <+88>: movq %r14, %rdi
0x116156e3b <+91>: callq 0x116340642 ; symbol stub for: pthread_mutex_unlock
0x116156e40 <+96>: cmpb $0x0, 0x60(%rbx)
0x116156e44 <+100>: je 0x116156e86 ; <+166>
0x116156e46 <+102>: movq 0x31d47b(%rip), %rsi ; "willChangeValueForKey:"
0x116156e4d <+109>: movq 0x27494c(%rip), %r14 ; (void *)0x00000001180cfac0: objc_msgSend
...
0x116156e63 <+131>: callq 0x11633fee0 ; symbol stub for: class_getMethodImplementation
...
0x116156e74 <+148>: movq 0x31d465(%rip), %rsi ; "didChangeValueForKey:"
0x116156e7b <+155>: movq %r13, %rdi
0x116156e7e <+158>: movq %r12, %rdx
0x116156e81 <+161>: callq *%r14
0x116156e84 <+164>: jmp 0x116156ee6 ; <+262>
0x116156e86 <+166>: movq 0x274443(%rip), %rax ; (void *)0x0000000119f9c070: _NSConcreteStackBlock
0x116156e8d <+173>: leaq -0x70(%rbp), %r9
0x116156e91 <+177>: movq %rax, (%r9)
0x116156e94 <+180>: movl $0xc2000000, 0x8(%r9) ; imm = 0xC2000000
0x116156e9c <+188>: movl $0x0, 0xc(%r9)
0x116156ea4 <+196>: leaq 0x191(%rip), %rax ; ___NSSetObjectValueAndNotify_block_invoke
0x116156eab <+203>: movq %rax, 0x10(%r9)
0x116156eaf <+207>: leaq 0x278b6a(%rip), %rax ; __block_descriptor_tmp.44
...
0x116156ece <+238>: movq 0x31df0b(%rip), %rsi ; "_changeValueForKey:key:key:usingBlock:"
0x116156ed5 <+245>: xorl %ecx, %ecx
0x116156ed7 <+247>: xorl %r8d, %r8d
0x116156eda <+250>: movq %r13, %rdi
0x116156edd <+253>: movq %r12, %rdx
0x116156ee0 <+256>: callq *0x2748ba(%rip) ; (void *)0x00000001180cfac0: objc_msgSend
0x116156ee6 <+262>: movq 0x31c053(%rip), %rsi ; "release". // crash 位置
0x116156eed <+269>: movq %r12, %rdi
0x116156ef0 <+272>: callq *0x2748aa(%rip) ; (void *)0x00000001180cfac0: objc_msgSend
....
出现这个问题的原因在于 注册了两次kvo 但是仅仅是释放了一次,这是其中的一个问题,但是kvo 不是万能的因为在
nm -a /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation
00000000000e63fa t __NSSetBoolValueAndNotify
000000000006d515 t __NSSetCharValueAndNotify
000000000006d3bb t __NSSetDoubleValueAndNotify
00000000000e768b t __NSSetFloatValueAndNotify
00000000000e756b t __NSSetIntValueAndNotify
0000000000075e6b t __NSSetLongLongValueAndNotify
000000000017c4af t __NSSetLongValueAndNotify
000000000006f3c6 t __NSSetObjectValueAndNotify
00000000000e18ef t __NSSetPointValueAndNotify
000000000017c93f t __NSSetRangeValueAndNotify
00000000000d631c t __NSSetRectValueAndNotify
000000000017c6f7 t __NSSetShortValueAndNotify
00000000000de41f t __NSSetSizeValueAndNotify
000000000017c38c t __NSSetUnsignedCharValueAndNotify
00000000000f6f53 t __NSSetUnsignedIntValueAndNotify
00000000000d40d7 t __NSSetUnsignedLongLongValueAndNotify
000000000017c5d3 t __NSSetUnsignedLongValueAndNotify
000000000017c81b t __NSSetUnsignedShortValueAndNotify
000000000017b7e4 t __NSSetValueAndNotifyForKeyInIvar
000000000017b846 t __NSSetValueAndNotifyForUndefinedKey
Foundation 提供了大部分基础数据类型的辅助函数(Objective C中的 Boolean 只是 unsigned char 的 typedef,所以包括了,但没有 C++中的 bool),此外还包括一些常见的 Cocoa 结构体如 Point, Range, Rect, Size,这表明这些结构体也可以用于自动键值观察,但要注意除此之外的结构体就不能用于自动键值观察了。对于所有 Objective C 对象对应的是 __NSSetObjectValueAndNotify。
总结:在使用kvo 的时候注意一点,一定是注册一个删除一个,不能说注册两次而销毁一次,另外最坑的是,crash 的地方和监听的属性没有直接关系,如图,注册两次的监听 A属性,但是crash 出现在B 上面
需要深入的同学可以看如下文档
Key-value observing:官方文档
Key-Value Observing Done Right : 官方 KVO 实现的缺陷
关于iOS中的kvo错误 _NSSetObjectValueAndNotify