如何测试Objective-C对象是否可以在ARC下使用弱引用?

发布于 2025-01-04 23:02:29 字数 2479 浏览 4 评论 0原文

苹果提到了两种方法 supportsWeakPointers记录在 ARC 的发行说明中,但在实际运行时和框架中从未提及。还可以看出,这种方法实际上在实践中被忽略。另一种方法是allowsWeakReference,它没有在任何地方记录,但在 NSObject.h 中声明如下。

- (BOOL)allowsWeakReference NS_UNAVAILABLE;
- (BOOL)retainWeakReference NS_UNAVAILABLE;

尝试在运行时调用 allowsWeakReference 会导致程序崩溃并显示以下堆栈跟踪

objc[17337]: Do not call -_isDeallocating.

#0  0x00007fff9900f768 in _objc_trap ()
#1  0x00007fff9900f8aa in _objc_fatal ()
#2  0x00007fff9901bd90 in _objc_rootIsDeallocating ()
#3  0x00007fff97e92ce9 in -[NSObject _isDeallocating] ()
#4  0x00007fff97b5fad5 in -[NSObject(NSObject) allowsWeakReference] ()
#5  0x00007fff97dfe021 in -[NSObject performSelector:] ()
...
...
#11 0x00007fff97a5fd32 in __-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_1 ()
#12 0x00007fff97dafaaa in _CFXNotificationPost ()
#13 0x00007fff97a4bfe7 in -[NSNotificationCenter postNotificationName:object:userInfo:] ()
#14 0x00007fff8fa0460f in -[NSApplication _postDidFinishNotification] ()
#15 0x00007fff8fa04375 in -[NSApplication _sendFinishLaunchingNotification] ()
#16 0x00007fff8fa0303c in -[NSApplication(NSAppleEventHandling) _handleAEOpenEvent:] ()
#17 0x00007fff8fa02d9d in -[NSApplication(NSAppleEventHandling) _handleCoreEvent:withReplyEvent:] ()
#18 0x00007fff97df9591 in -[NSObject performSelector:withObject:withObject:] ()
#19 0x00007fff97a827eb in __-[NSAppleEventManager setEventHandler:andSelector:forEventClass:andEventID:]_block_invoke_1 ()
#20 0x00007fff97a81772 in -[NSAppleEventManager dispatchRawAppleEvent:withRawReply:handlerRefCon:] ()
#21 0x00007fff97a81600 in _NSAppleEventManagerGenericHandler ()
#22 0x00007fff96623c25 in aeDispatchAppleEvent ()
#23 0x00007fff96623b03 in dispatchEventAndSendReply ()
#24 0x00007fff966239f7 in aeProcessAppleEvent ()
#25 0x00007fff92101af9 in AEProcessAppleEvent ()
#26 0x00007fff8fa001a9 in _DPSNextEvent ()
#27 0x00007fff8f9ff861 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] ()
#28 0x00007fff8f9fc19d in -[NSApplication run] ()
#29 0x00007fff8fc7ab88 in NSApplicationMain ()

那么,如果这两种方法都无法使用,如何测试对象是否支持对其形成弱引用呢?

Apple mentions two methods supportsWeakPointers which is documented in the release notes for ARC but never mentioned in the actual runtime and frameworks. It has also be observed that this method is in fact ignored in practice. Another method is allowsWeakReference, which is not documented anywhere but declared in NSObject.h as follows.

- (BOOL)allowsWeakReference NS_UNAVAILABLE;
- (BOOL)retainWeakReference NS_UNAVAILABLE;

Trying to call allowsWeakReference during runtime causes program to crash with the following stack trace

objc[17337]: Do not call -_isDeallocating.

#0  0x00007fff9900f768 in _objc_trap ()
#1  0x00007fff9900f8aa in _objc_fatal ()
#2  0x00007fff9901bd90 in _objc_rootIsDeallocating ()
#3  0x00007fff97e92ce9 in -[NSObject _isDeallocating] ()
#4  0x00007fff97b5fad5 in -[NSObject(NSObject) allowsWeakReference] ()
#5  0x00007fff97dfe021 in -[NSObject performSelector:] ()
...
...
#11 0x00007fff97a5fd32 in __-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_1 ()
#12 0x00007fff97dafaaa in _CFXNotificationPost ()
#13 0x00007fff97a4bfe7 in -[NSNotificationCenter postNotificationName:object:userInfo:] ()
#14 0x00007fff8fa0460f in -[NSApplication _postDidFinishNotification] ()
#15 0x00007fff8fa04375 in -[NSApplication _sendFinishLaunchingNotification] ()
#16 0x00007fff8fa0303c in -[NSApplication(NSAppleEventHandling) _handleAEOpenEvent:] ()
#17 0x00007fff8fa02d9d in -[NSApplication(NSAppleEventHandling) _handleCoreEvent:withReplyEvent:] ()
#18 0x00007fff97df9591 in -[NSObject performSelector:withObject:withObject:] ()
#19 0x00007fff97a827eb in __-[NSAppleEventManager setEventHandler:andSelector:forEventClass:andEventID:]_block_invoke_1 ()
#20 0x00007fff97a81772 in -[NSAppleEventManager dispatchRawAppleEvent:withRawReply:handlerRefCon:] ()
#21 0x00007fff97a81600 in _NSAppleEventManagerGenericHandler ()
#22 0x00007fff96623c25 in aeDispatchAppleEvent ()
#23 0x00007fff96623b03 in dispatchEventAndSendReply ()
#24 0x00007fff966239f7 in aeProcessAppleEvent ()
#25 0x00007fff92101af9 in AEProcessAppleEvent ()
#26 0x00007fff8fa001a9 in _DPSNextEvent ()
#27 0x00007fff8f9ff861 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] ()
#28 0x00007fff8f9fc19d in -[NSApplication run] ()
#29 0x00007fff8fc7ab88 in NSApplicationMain ()

So, how can one test whether an object supports the forming of weak reference to it then, if neither method can be used?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

心如荒岛 2025-01-11 23:02:29

最终一起破解了这个。适合我需要的东西。

@implementation NSObject (MAWeakReference)

static NSSet *weakRefUnavailableClasses = nil;

+ (void)load {
    // https://developer.apple.com/library/mac/#releasenotes/ObjectiveC/RN-TransitioningToARC/_index.html
    weakRefUnavailableClasses = [NSSet setWithObjects:
                                 // Classes that don't support zeroing-weak references
                                 @"NSATSTypesetter",
                                 @"NSColorSpace",
                                 @"NSFont",
                                 @"NSFontManager",
                                 @"NSFontPanel",
                                 @"NSImage",
                                 @"NSMenuView",
                                 @"NSParagraphStyle",
                                 @"NSSimpleHorizontalTypesetter",
                                 @"NSTableCellView",
                                 @"NSTextView",
                                 @"NSViewController",
                                 @"NSWindow",
                                 @"NSWindowController",
                                 // In addition
                                 @"NSHashTable",
                                 @"NSMapTable",
                                 @"NSPointerArray",
                                 // TODO: need to add all the classes in AV Foundation
                                 nil];
}

- (BOOL)ma_supportsWeakPointers {
    if ([self respondsToSelector:@selector(supportsWeakPointers)])
        return [[self performSelector:@selector(supportsWeakPointers)] boolValue];

    // NOTE: Also test for overriden implementation of allowsWeakReference in NSObject subclass.
    // We must use a bit of hackery here because by default NSObject's allowsWeakReference causes
    // assertion failure and program crash if it is not called by the runtime
    Method defaultMethod = class_getInstanceMethod([NSObject class], @selector(allowsWeakReference));
    Method overridenMethod = class_getInstanceMethod([self class], @selector(allowsWeakReference));
    if (overridenMethod != defaultMethod)
        return [[self performSelector:@selector(allowsWeakReference)] boolValue];

    // Make sure we are not one of classes that do not support weak references according to docs
    for (NSString *className in weakRefUnavailableClasses)
        if ([self isKindOfClass:NSClassFromString(className)])
            return NO;

    // Finally, all tests pass, by default objects support weak pointers
    return YES;
}

@end

Ended up hacking this together. Works for what I need it for.

@implementation NSObject (MAWeakReference)

static NSSet *weakRefUnavailableClasses = nil;

+ (void)load {
    // https://developer.apple.com/library/mac/#releasenotes/ObjectiveC/RN-TransitioningToARC/_index.html
    weakRefUnavailableClasses = [NSSet setWithObjects:
                                 // Classes that don't support zeroing-weak references
                                 @"NSATSTypesetter",
                                 @"NSColorSpace",
                                 @"NSFont",
                                 @"NSFontManager",
                                 @"NSFontPanel",
                                 @"NSImage",
                                 @"NSMenuView",
                                 @"NSParagraphStyle",
                                 @"NSSimpleHorizontalTypesetter",
                                 @"NSTableCellView",
                                 @"NSTextView",
                                 @"NSViewController",
                                 @"NSWindow",
                                 @"NSWindowController",
                                 // In addition
                                 @"NSHashTable",
                                 @"NSMapTable",
                                 @"NSPointerArray",
                                 // TODO: need to add all the classes in AV Foundation
                                 nil];
}

- (BOOL)ma_supportsWeakPointers {
    if ([self respondsToSelector:@selector(supportsWeakPointers)])
        return [[self performSelector:@selector(supportsWeakPointers)] boolValue];

    // NOTE: Also test for overriden implementation of allowsWeakReference in NSObject subclass.
    // We must use a bit of hackery here because by default NSObject's allowsWeakReference causes
    // assertion failure and program crash if it is not called by the runtime
    Method defaultMethod = class_getInstanceMethod([NSObject class], @selector(allowsWeakReference));
    Method overridenMethod = class_getInstanceMethod([self class], @selector(allowsWeakReference));
    if (overridenMethod != defaultMethod)
        return [[self performSelector:@selector(allowsWeakReference)] boolValue];

    // Make sure we are not one of classes that do not support weak references according to docs
    for (NSString *className in weakRefUnavailableClasses)
        if ([self isKindOfClass:NSClassFromString(className)])
            return NO;

    // Finally, all tests pass, by default objects support weak pointers
    return YES;
}

@end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文