3

I always see people debating whether or not to use a property's setter in the -init method. My problem is how to create a default value in a subclass for an inherited property. Say we have a class called NSLawyer -- a framework class, that I can't change -- with an interface that looks like this:

@interface NSLawyer : NSObject {
    @private
    NSUInteger _numberOfClients;
}

@property (nonatomic, assign) NSUInteger numberOfClients;

@end

And an implementation that looks like this:

@implementation NSLawyer

- (instancetype)init
{
    self = [super init];
    if (self) {
        _numberOfClients = 0;
    }
    return self;
}

@end

Now let's say I want to extend NSLawyer. My subclass will be called SeniorPartner. And since a senior partner should have lots of clients, when SeniorPartner gets initialized, I don't want the instance to start with 0; I want it to have 10. Here's SeniorPartner.m:

@implementation SeniorPartner

- (instancetype)init
{
    self = [super init];
    if (self) {

        // Attempting to set the ivar directly will result in the compiler saying, 
        // "Instance variable _numberOfClients is private."

        // _numberOfClients = 10; <- Can't do this.

        // Thus, the only way to set it is with the mutator:
        self.numberOfClients = 10;

        // Or: [self setNumberOfClients:10];
    }
    return self;
}

@end

So what's a Objective-C newcomer to do? Well, I mean, there's only one thing I can do, and that's set the property. Unless there's something I'm missing. Any ideas, suggestions, tips, or tricks?

Ben Stock
  • 1,986
  • 1
  • 22
  • 30
  • 1
    perhaps change `@private` to `@protected` – Kevin DiTraglia May 30 '14 at 02:52
  • @KevinDiTraglia I should have mentioned that my issue stems from not being able to modify the superclass. I'm subclassing AppKit classes like `NSButton` or `NSPathControl`, and because our good friends at Apple like to watch me hurt, all of their classes have their ivars in their header files (almost as to mock me), but I can't touch them. :-( What about those situations? – Ben Stock May 30 '14 at 04:13
  • Can you use the c arrow operator? `self->numberOfFriends`? – stevesliva May 30 '14 at 04:49
  • @stevesliva I actually tried that and I got the same error. :-( I'm just surprised that nobody has really mentioned this before. Am I the only person who subclasses Apple classes? It's been a huge pain in the ass, but it's not like when I was forced into working with a Magento installation after knowing HTML for a few months. If you want to know pain. That's pain. – Ben Stock May 30 '14 at 06:51
  • 2
    Yes we subclass Apple classes all the time. But accessing private ivars and methods is a really bad idea, they may change at any time--don't do it. It is only recently that ivars can be declared in the implementation so legacy code has them in the .h file. In most cases Apple puts private ivars and methods in a private .h file that is not provided. – zaph May 30 '14 at 10:06
  • 1
    @BenStock Is it safe to assume that in real life, your `init` methods return `self`? – jlehr May 30 '14 at 18:17
  • @jlehr Haha, I should probably fix that, huh? Thanks for the heads-up. – Ben Stock May 31 '14 at 05:44
  • @Zaph Yeah, I get that. That's what I'm saying … I don't want to access private ivars. I just want to initialize an ivar from a superclass with a different value. That's all. I guess my biggest problems with Obj-C thus far have more to do with that fact that Apple still has a bunch of legacy code intermixed with new code, so being new to the language (and Cocoa), it can get confusing in regard to best practices and what not. Thank you for helping me wade through the old stuff! – Ben Stock May 31 '14 at 05:49

2 Answers2

3

You should do exactly has you have; call the accessor. The declaring class typically avoids calling its own accessors in init to avoid accidentally calling an overridden accessor in a subclass that might rely on the consistency of data you haven't initialized yet. Your superclass on the other hand should be completely consistent by the time the subclass's init is run, so there is no problem using superclass accessors at that time.

Consider the common and general case: you want to set your transform in a UIView subclass. How would you solve that other than call setTransform:? Subclassing non-Apple code is no different.

jscs
  • 63,694
  • 13
  • 151
  • 195
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
0

Make it @protected. It is very rare to make an ivar or property private these days. Private ivars and/or properties are better declared in the implementation. For that matter @protected ivars/properties are rarely seen in Objective-C but perfectly fine.

Using a setter either as a method or with dot notation is just wrong (yes it works but is really bad form), if you want use setters/getters declare a property.

zaph
  • 111,848
  • 21
  • 189
  • 228
  • When I want something "private," I usually extend the class `@interface` in the implementation (which is what I think you mean by "declared in the implementation"), but if you see my comment to Kevin (above), I personally can't change the superclass. I totally forgot to mention that in my initial post. I'm not sure what your last sentence means exactly. What's the point of a setter if not to "set." If I have a property `color`, I can't say `self.color = [NSColor redColor];`? I thought Apple recommended using accessors/mutators within the class for KVC. Or are you talking about just in `-init`? – Ben Stock May 30 '14 at 04:18
  • If you have a property `color` you can say `self.color = [NSColor redColor];`, that is shorthand for `[self setColor:[NSColor redColor]];`, it is not the the "C" usage of ".". – zaph May 30 '14 at 09:53
  • I got ya. I misunderstood what you meant. My bad. Thanks for your input! – Ben Stock May 31 '14 at 05:43