Home > other >  Why does a constant property may get initialized twice in a Swift class?
Why does a constant property may get initialized twice in a Swift class?

Time:12-05

The project I'm working on is a mix of Swift and Objective-C. Here's the snippet:

// ViewController.m
@interface ViewController ()

@property (nonatomic, strong) MyModel *model;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.model = [[MyModel alloc] initWithIntValue:10];
}

// MyModel.swift
fileprivate class SomeProperty {
    init() {
        print("SomeProperty init")
    }
}

class MyModel: BaseModel {
    private let property = SomeProperty()
}

// BaseModel.h
@interface BaseModel : NSObject

- (instancetype)initWithIntValue:(int)intValue;
- (instancetype)initWithIntValue:(int)intValue doubleValue:(double)doubleValue;

@end

// BaseModel.m
@implementation BaseModel

- (instancetype)initWithIntValue:(int)intValue doubleValue:(double)doubleValue {
    if (self = [super init]) {
        
    }
    return self;
}

- (instancetype)initWithIntValue:(int)intValue {
    return [self initWithIntValue:intValue doubleValue:0];
}

@end

Interestingly, I find when MyModel instance is initialized, SomeProperty init will be printed twice, which means two SomeProperty instances are created. What's worse, Debug Memory Graph shows that there is a SomeProperty object memory leak. So why is this and how can I fix it?

CodePudding user response:

Rewrite BaseModel.h like this:

- (instancetype)initWithIntValue:(int)intValue;
- (instancetype)initWithIntValue:(int)intValue doubleValue:(double)doubleValue NS_DESIGNATED_INITIALIZER;

Note the NS_DESIGNATED_INITIALIZER marker at the end of the second initializer. (You may have to scroll my code in order to see it.)

This marker, aside from what it does within Objective-C (in its role as a macro), tells Swift that both initializers are not designated initializers; rather, Swift concludes, the first one is a convenience initializer. And that is correct; it calls another initializer, namely — in this case — the designated initializer.

Without that NS_DESIGNATED_INITIALIZER markup, Swift interprets the situation incorrectly because of the (already rather complicated) relationship between Swift initializers and Objective-C initializers. It thinks both initializers are designated initializers and you get this curious double initialization from Swift.

  • Related