Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Synthesis and Protected Instance Variables in "Modern" Objective-C?

I want to create a class that serves as a base (or "abstract") class to be extended by subclasses. The best way I can explain what I'm talking about is with a few examples. Here's a possible interface for my superclass:

#import <Cocoa/Cocoa.h>
#import "MyViewControllerDelegate.h"

@interface MyViewController : NSViewController

@property (nonatomic, weak) id<MyViewModeControllerDelegate> delegate;
@property (nonatomic, copy) NSArray *content;

@end

Writing it like that seems nice and clean, but I can't access the ivars from my subclasses.

After doing some research, I've concluded that a good way to provide subclasses with direct access to ivars is to use the @protected directive and include any declarations in the header file so subclasses can see it:

#import <Cocoa/Cocoa.h>
#import "MyViewControllerDelegate.h"

@interface MyViewController : NSViewController {
@protected
    __weak id<MyViewControllerDelegate> _delegate;
    NSMutableArray *_content;
}

@property (nonatomic, weak) id<BSDViewModeControllerDelegate> delegate;
@property (nonatomic, copy) NSArray *content;

@end

I personally don't have an issue with that, and it seems to work the way I want it to (e.g. subclasses can access the ivars directly, but other classes have to use accessors). However, I read blog posts or Stack Overflow answers every day that say instance variables should just be synthesized, or "I don't even touch instance variables anymore."

The thing is, I started learning Objective-C post-ARC, so I'm not fully aware of the ways in which developers had to do things in the past. I personally like the control I have when I implement my own getters/setters, and I like being able to actually see instance variable declarations, but maybe I'm old school. I mean, if one should "just let the compiler synthesize the instance variables," how does one include any sort of logic or "side effects" without implementing a bunch of KVO?

For example, if my instance variables and getters/setters are synthesized, how do I initialize stuff lazily? For example, I sometimes like to do this:

- (NSArray *)myLazyArray
{
    if ( _myLazyArray == nil ) {
        self.myLazyArray = @[];
    }
    return _myLazyArray.copy;
}

Or how do I make sure that a value being set isn't the same as the currently set value? I'll sometimes implement a check in my mutator method like this:

- (void)setMyLazyArray:(NSArray *)array
{
    if ( [array isEqualToArray:_myLazyArray] )
        return;
    _myLazyArray = array.mutableCopy;
}

I've read all of Apple's documentation, but half their docs date back to 2008 (or worse in some cases), so I'm not exactly sure they're the best place to get information on the matter.

I guess the gist of my question is this: Is there a preferred "modern" way of handling instance variables, variable synthesis, inheritance, scope, etc. in Objective-C? Bonus points for answers that don't include "Bro, Swift." or "You aren't using Swift?"

Any guidance would be much appreciated. Thanks for reading!

like image 463
Ben Stock Avatar asked Jan 26 '26 18:01

Ben Stock


1 Answers

Why do your subclasses need access to your ivars? Ivars are an implementation detail and subclasses shouldn't be concerned with that. There could be all sorts of side effects if the parent class is doing logic in the property setter/getters. Therefore, always access them through the property.

Assuming this is in your subclass and you are overriding a property getter:

- (NSArray *)myLazyArray
{
    if ( super.myLazyArray == nil ) {
        // do what you need to do to populate the array
        // assign it to yourself (or super)
        self.myLazyArray = @[];
    }

    return super.myLazyArray;
}

And then for the setter:

- (void)setMyLazyArray:(NSArray *)array
{
    if ( [array isEqualToArray:super.myLazyArray] )
        return;
    super.myLazyArray = array.mutableCopy;
}
like image 108
Jon Gilkison Avatar answered Jan 28 '26 09:01

Jon Gilkison



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!