【问题标题】:is method_setImplementation() atomic?method_setImplementation() 是原子的吗?
【发布时间】:2016-05-25 11:44:20
【问题描述】:

如果method_setImplementation()正在替换NSObject类的dealloc方法,同时在另一个线程中,一个NSObject实例正在调用或即将调用dealloc方法会发生什么。

实例会得到dealloc方法的正确地址吗? 使用method swizzling时随机崩溃。调用dealloc方法时总是崩溃。

这是我的崩溃信息:

Exception Type:  EXC_BAD_ACCESS (SIGBUS)
Exception Codes: 0x00000000 at 0x0000000000000000

和崩溃的线程:

Thread 0:
0   libsystem_kernel.dylib          0x00000001837b7ffc 0x18379c000 + 114684 (__psynch_rw_unlock + 8)
1   libsystem_pthread.dylib         0x0000000183881b1c 0x183880000 + 6940 (pthread_rwlock_unlock + 380)
2   libobjc.A.dylib                 0x000000018328f718 0x18327c000 + 79640 (<redacted> + 20)
3   libobjc.A.dylib                 0x00000001832894a0 0x18327c000 + 54432 (method_setImplementation + 64)
4   MyApp                         0x0000000100904824 0x1000b4000 + 8718372 (kszombie_install + 204)
5   MyApp                         0x0000000100448248 0x1000b4000 + 3752520 (-[DYCrashReportManager configureAdvancedSettings] + 60)
6   MyApp                         0x000000010044756c 0x1000b4000 + 3749228 (+[DYCrashReportManager setDefaultKSCrashHander] + 100)
7   MyApp                         0x00000001000e15b4 0x1000b4000 + 185780 (-[AppDelegate application:didFinishLaunchingWithOptions:] + 700)
8   UIKit                           0x0000000188dde8a8 0x188d54000 + 567464 (<redacted> + 400)
9   UIKit                           0x000000018900e094 0x188d54000 + 2859156 (<redacted> + 2904)
10  UIKit                           0x0000000189012500 0x188d54000 + 2876672 (<redacted> + 1684)
11  UIKit                           0x000000018900f674 0x188d54000 + 2864756 (<redacted> + 168)
12  FrontBoardServices              0x00000001855bf7ac 0x185598000 + 161708 (<redacted> + 36)
13  FrontBoardServices              0x00000001855bf618 0x185598000 + 161304 (<redacted> + 168)
14  FrontBoardServices              0x00000001855bf9c8 0x185598000 + 162248 (<redacted> + 56)
15  CoreFoundation                  0x0000000183bd5124 0x183af4000 + 921892 (<redacted> + 24)
16  CoreFoundation                  0x0000000183bd4bb8 0x183af4000 + 920504 (<redacted> + 540)
17  CoreFoundation                  0x0000000183bd28b8 0x183af4000 + 911544 (<redacted> + 724)
18  CoreFoundation                  0x0000000183afcd10 0x183af4000 + 36112 (CFRunLoopRunSpecific + 384)
19  UIKit                           0x0000000188dd7834 0x188d54000 + 538676 (<redacted> + 460)
20  UIKit                           0x0000000188dd1f70 0x188d54000 + 515952 (UIApplicationMain + 204)
21  DuoYiIM                         0x00000001000c4300 0x1000b4000 + 66304 (main + 132)
22  libdyld.dylib                   0x000000018369a8b8 0x183698000 + 10424 (<redacted> + 4)

Thread 2 Crashed:
0   (null) 0x0000000000000000 0x0 + 0
1   CoreFoundation                  0x0000000183b01f14 0x183af4000 + 57108 (<redacted> + 148)
2   libobjc.A.dylib                 0x000000018329dae8 0x18327c000 + 137960 (<redacted> + 508)
3   Foundation                      0x00000001846dc844 0x184500000 + 1951812 (<redacted> + 1028)
4   libxpc.dylib                    0x00000001838b4b4c 0x1838b0000 + 19276 (<redacted> + 28)
5   libxpc.dylib                    0x00000001838b4af0 0x1838b0000 + 19184 (<redacted> + 40)
6   libdispatch.dylib               0x000000018366947c 0x183668000 + 5244 (<redacted> + 16)
7   libdispatch.dylib               0x00000001836754c0 0x183668000 + 54464 (<redacted> + 864)
8   libdispatch.dylib               0x000000018366cf80 0x183668000 + 20352 (<redacted> + 464)
9   libdispatch.dylib               0x000000018366947c 0x183668000 + 5244 (<redacted> + 16)
10  libdispatch.dylib               0x0000000183677914 0x183668000 + 63764 (<redacted> + 2140)
11  libdispatch.dylib               0x00000001836770b0 0x183668000 + 61616 (<redacted> + 112)
12  libsystem_pthread.dylib         0x0000000183881470 0x183880000 + 5232 (_pthread_wqthread + 1092)

我得到了一个空指针。

有没有安全的方法来代替dealloc方法?

【问题讨论】:

  • 在堆栈跟踪中编辑有关 libobjc.dylib 中符号的信息无论如何都没有用。我们绝对无法将这些地址符号化为任何有用的东西。
  • @Richard J. Ross III。我使用 KSCrash 来获取我的崩溃报告。我发现每次崩溃时,x1 寄存器中都有一个 'dealloc' 字符串。这意味着系统正在调用 dealloc方法。问题一定是方法调配,但我不知道确切的原因。我只是怀疑可能 method_setImplementation() 不是原子的,所以我来寻求帮助。

标签: ios objective-c objective-c-runtime method-swizzling


【解决方案1】:

objc 运行时操作方法一般是原子的,但原子性并不意味着线程安全。线程 A 在线程 B 使用对象的时候乱搞对象的实现总是很危险的,因为无法保证线程 B 会使用新的还是旧的实现。

不过,总的来说,swizzling 方法很糟糕,swizzling NSObject 的方法更糟糕

【讨论】:

  • 在 ARC 下是否对 swizzling dealloc 进行了很好的定义?我认为当类没有直接定义它时,编译器会优化掉大部分。
  • @RobNapier 是的;我怀疑通过 swizzling 处理 dealloc 可能会在 ARC 下挖得更深,但我认为它并没有完全破裂。 IE。因为基本上任何方法都可以从跨模块边界调用,ARC 必须始终与非 ARC 兼容,反之亦然。
【解决方案2】:

我想我找到了崩溃的原因。 那是因为 KSCrash 使用了dealloc 方法来检测僵尸对象。

static IMP g_originalDealloc_NSObject;
static void handleDealloc_NSObject(id self, SEL _cmd)
{
    handleDealloc(self);
    typedef void (*fn)(id,SEL);
    fn f = (fn)g_originalDealloc_NSObject;
    f(self, _cmd);
}
static void installDealloc_NSObject()
{
    g_originalDealloc_NSObject = method_setImplementation(class_getInstanceMethod([NSObject class], @selector(dealloc)),(IMP)handleDealloc_NSObject);
}

崩溃发生在installDealloc_NSObject(),method_setImplementation() 确实更改了实现,但还没有将旧实现返回到g_originalDealloc_NSObject。同时在另一个线程中,一个对象调用了dealloc,所以在handleDealloc_NSObject() 中出现空指针错误。

【讨论】:

    猜你喜欢
    • 2011-03-03
    • 2010-12-11
    • 2020-05-07
    • 2013-01-26
    • 2019-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多