Today I encountered compatibility issues with Objective-C Override alloc while mixing.

Problem description

In Swift the alloc method is disabled, alloc and init are automatically merged, and memory allocation is handled internally. And when you instantiate Objective-C in Swift, you don’t call the alloc method, you call the constructor method directly.

+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
Copy the code

This leads to some code incompatibilities, such as defining a DefaultLoadingView in the base library.

@interface DefaultLoadingView : UIView <LoadingViewProtocol>
@end

@interface DefaultLoadingView (sub)
+ (Class)defaultLoadingViewClass4Sub;
@end

@implementation DefaultLoadingView
+ (instancetype)alloc {
    if ([self respondsToSelector:@selector(defaultLoadingViewClass4Sub)]) {
        Class tempClass = [self defaultLoadingViewClass4Sub];
        if (tempClass) {
            return[tempClass alloc]; }}return [super alloc];
}
@end
Copy the code

A business can customize a loadingView by implementing the DefaultLoadingView+sub classification.

@implementation DefaultLoadingView (sub)
+ (Class)defaultLoadingViewClass4Sub {
    return CustomLoadingView.class;
}
@end
Copy the code

In Objective-C we can create a loadingView instance directly by [[DefaultLoadingView alloc] init]. If the business side has a custom one, it creates a CustomLoadingView. Otherwise it is DefaultLoadingView.

However, any instance created from DefaultLoadingView() in Swift will be of type DefaultLoadingView because it does not call the alloc method.

The solution

1. Package the middle layer in Objective-C if you want to keep alloc. For example, here we can add a factory method that calls the alloc and constructor methods, rename the factory method with NS_SWIFT_NAME and import it into Swift as a constructor for easy use, Also using NS_SWIFT_UNAVAILABLE marks the original constructor method as unavailable in Swift and instructs the caller to use the new constructor method.

@interface DefaultLoadingView : UIView <LoadingViewProtocol>
+ (instancetype)loadingViewWithFrame:(CGRect)frame NS_SWIFT_NAME(init(swift_frame:));
- (instancetype)initWithFrame:(CGRect)frame NS_SWIFT_UNAVAILABLE("Use init(swift_frame:) instead");
@end
  
@implementation DefaultLoadingView
+ (instancetype)loadingViewWithFrame:(CGRect)frame {
    return [[self alloc] initWithFrame:frame];
}
@end
  
// Use the new constructor method
let loadingView = DefaultLoadingView.init(swift_frame: frame)
Copy the code

The downside of this scenario is that if you have multiple constructor methods originally, you need to add multiple corresponding factory methods and add NS_SWIFT_NAME and NS_SWIFT_UNAVAILABLE.

2. For this scenario, there is an alternative — simply fetch the desired class from Swift, discard the alloc scheme, or leave it for backward compatibility. DefaultImplClass DefaultLoadingView is registered with the LoadingViewProtocol in the base library using the protocol factory (which maintains the mapping between Protocol and implClass). The business can register implClass CustomLoadingView for the protocol. If implClass is not registered, the default defaultImplClass or defaultImplInstance is used.

@implementation DefaultLoadingView
+ (void)load {
    [ServiceFactory registerService:@protocol(LoadingViewProtocol) defaultImplClass:self.class];
}
@end
  
@implementation CustomLoadingView
+ (void)load {
    [ServiceFactory registerService:@protocol(LoadingViewProtocol) implClass:CustomLoadingView.class];
}
@end
  
// Created by the protocol factory
let loadingView = ServiceFactory.createService(LoadingViewProtocol.self, initBlock: nil) as! UIView & LoadingViewProtocol
Copy the code

Do you have a better plan? Welcome to leave a message.

Recommended reading

  • Column | iOS remix
  • Add enumeration macros to Objective-C to improve the mix experience
  • Use an Objective-C typedef NSString as a String bridge to Swift
  • IOS mashup | Rename objective-C API for Swift
  • IOS mashup | Improved Objective-C API for Swift
  • IOS mashup | Limit API availability
  • IOS mashup | specifies nullability for objective-C apis
  • IOS override alloc compatibility issue