首页 > 代码库 > 关于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