I have XIBs that contain custom objects, one of these is actually a class cluster whose -init
method always returns the same singleton object.
Basically:
- (instancetype)init
{
self = [super init];
if (HelpLinkHelperSingleton==nil)
{
// This is the first instance of DDHelpLink: make it the immortal singleton
HelpLinkHelperSingleton = self;
}
else
{
// Not the first DDHelpLink object to be created: discard this instance
// and return a reference to the shared singleton
self = HelpLinkHelperSingleton;
}
return self;
}
Starting in macOS 12.0.1, loading the XIB throws this exception:
This coder is expecting the replaced object 0x600002a4f680 to be returned from NSClassSwapper.initWithCoder instead of <DDHelpLink: 0x600002a487a0>
I tried implementing <NSSecureCoding>
and doing the same thing, but that doesn't work either.
Is there still a way to use class clusters in NIBs?
I worked around this problem by using a proxy object in the XIB that forwards the messages to the singleton.
@interface HelpLinkHelperProxy : NSObject
@end
@implementation HelpLinkHelperProxy
{
HelpLinkHelper* _singleton;
}
- (void) forwardInvocation:(NSInvocation*)invocation
{
if (_singleton == nil)
{
_singleton = [HelpLinkHelper new];
}
if ([_singleton respondsToSelector:[invocation selector]])
{
[invocation invokeWithTarget:_singleton];
}
else
{
[super forwardInvocation:invocation];
}
}
@end
If we were to subclass from NSProxy
instead of NSObject
, the solution would look like this:
@interface HelpLinkHelperProxy : NSProxy
@end
@implementation HelpLinkHelperProxy
{
HelpLinkHelper* _singleton;
}
- (instancetype) init
{
_singleton = [HelpLinkHelper new];
return self;
}
- (NSMethodSignature*) methodSignatureForSelector:(SEL)sel
{
return [_singleton methodSignatureForSelector:sel];
}
- (void) forwardInvocation:(NSInvocation*)invocation
{
if ([_singleton respondsToSelector:[invocation selector]])
{
[invocation invokeWithTarget:_singleton];
}
else
{
[super forwardInvocation:invocation];
}
}
+ (BOOL) respondsToSelector:(SEL)aSelector
{
return [HelpLinkHelper respondsToSelector:aSelector];
}
@end