Search code examples
objective-coptimizationstatic-analysislintoclint

How to reduce high cyclomatic complexity while initializing from NSUserDefaults


I have this initializer for an object:

-(id)init
{
    self = [super init];
    if (self) {
        if([[NSUserDefaults standardUserDefaults] objectForKey:kTermsAccepted] != nil){
            _termsAccepted = [[NSUserDefaults standardUserDefaults] boolForKey:kTermsAccepted];
        }
        if([[NSUserDefaults standardUserDefaults] objectForKey:kInitialSetupCompleted] != nil){
            _initialSetupCompleted = [[NSUserDefaults standardUserDefaults] boolForKey:kInitialSetupCompleted];
        }
        if([[NSUserDefaults standardUserDefaults] objectForKey:kDashboardMessage] != nil){
            _dashboardMessage = [[NSUserDefaults standardUserDefaults] objectForKey:kDashboardMessage];
        } else{
            _dashboardMessage = [[NSBundle mainBundle] localizedStringForKey:kDMDefaultDashboardMessage value:kDMDefaultDashboardMessage table:nil];
        }
        //50 other if statements
    }
    return self;
}

What would be a better way to do this so I don't get these warnings while doing an OCLint analysis?

Thanks!


Solution

  • All instance variables are initialised to 0; that means that BOOLs are initialised to NO. Therefore the effect of:

    if([[NSUserDefaults standardUserDefaults] objectForKey:kTermsAccepted] != nil){
        _termsAccepted = [[NSUserDefaults standardUserDefaults] boolForKey:kTermsAccepted];
    }
    

    ... is: if there is a stored value then set _termsAccepted to it. Otherwise _termsAccepted will be NO.

    boolForKey: has a documented return value of:

    If a boolean value is associated with defaultName in the user defaults, that value is returned. Otherwise, NO is returned.

    So compare and contrast with just:

    _termsAccepted = [[NSUserDefaults standardUserDefaults] boolForKey:kTermsAccepted];
    

    ... in that case the net result will be: if there is a stored value then set _termsAccepted to it. Otherwise _termsAccepted will be NO.

    So to reduce cyclomatic complexity, drop the if statements. They add nothing.

    EDIT: it's been correctly pointed out that I've missed the fact that BOOLs are not exclusively in use.

    Use -[NSUserDefaults registerDefaults:] to establish your fallback values. Those are kept in memory once set but not written to the store. Then the user defaults themselves will handle the "if no value stored, use this value" case for all types of object.