48

If I have this class defined, how do I access the someObject property in subclasses without compiler errors?

@interface MyBaseClass
  // someObject property not declared here because I want it to be scoped 
  // protected. Only this class instance and subclass instances should be
  // able to see the someObject property.
@end

// This is a private interface extension...properties declared here
// won't be visible to subclasses. However, I don't see any way to 
// declare protected properties...
@interface MyBaseClass (private)
   @property (nonatomic, readwrite, retain) NSObject *someObject;
@end

@interface MySubclass : MyBaseClass 
@end

@implementation MySubclass

- (id) init {
    // Try to do something with the super classes' someObject property. 
    // Always throws compile errors.

    // Semantic Issue: Property 'someObject' not found 
    // object of type 'MySubclass *'
    self.someObject = nil; 

}
@end



I'm obviously not understanding how inheritance works in objective-c. Could someone enlighten me?

memmons
  • 40,222
  • 21
  • 149
  • 183
  • 1
    @Elise - And I have flagged your comment because, while correct, it ignores the underlying conceptual question about how property inheritance works. Correcting code errors while ignoring the conceptual question the OP is actually asking is obtuse, frustrating, and not in the least bit useful. – Chuck Wolber Oct 19 '16 at 21:41

5 Answers5

57

The solution you're after is to declare the MyBaseClass private property in a class extension:

@interface MyBaseClass ()
@property (nonatomic, readwrite, retain) NSObject *someObject;
@end

You are then free to make that declaration both in MyBaseClass and in MySubclass. This lets MySubclass know about these properties so that its code can talk about them.

If the repetition bothers you, put the class extension in a .h file of its own and import it into both .m files.

I will give an example from my own code. Here is MyDownloaderPrivateProperties.h:

@interface MyDownloader ()
@property (nonatomic, strong, readwrite) NSURLConnection* connection;
@property (nonatomic, copy, readwrite) NSURLRequest* request;
@property (nonatomic, strong, readwrite) NSMutableData* mutableReceivedData;
@end

There is no corresponding .m file and that's all that's in this file; it is, as it were, purely declarative. Now here's the start of MyDownloader.m:

#import "MyDownloader.h"
#import "MyDownloaderPrivateProperties.h"
@implementation MyDownloader
@synthesize connection=_connection;
@synthesize request=_request;
@synthesize mutableReceivedData=_mutableReceivedData;
// ...

And here's the start of its subclass MyImageDownloader.m:

#import "MyImageDownloader.h"
#import "MyDownloaderPrivateProperties.h"

Problem solved. Privacy is preserved, as these are the only classes that import MyDownloaderPrivateProperties.h so they are the only classes that know about these properties as far as the compiler is concerned (and that's all that privacy is in Objective-C). The subclass can access the private properties whose accessors are synthesized by the superclass. I believe that's what you wanted to accomplish in the first place.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • this seems like the more direct answer to the general problem of how to let subclasses access the private interface of a superclass. the repetition does bother me, though. Might just make the properties public... – CharlieMezak Jul 23 '12 at 18:20
  • That seems like the best answer to me, not the one originally accepted . – Nir Golan Oct 07 '12 at 09:07
14

that's how you access them. how you declare them is what's biting you:

@interface MyBaseClass : NSObject
@property (nonatomic, readwrite, retain) NSObject *someObject;
@end

this is the normal way to declare a new objc class.

by adding the parentheses (instead of declaring the superclass - NSObject in this case), you have declared a class extension, which is probably not visible to the subclass (via inclusion).

you will probably never need to declare a root class in objc:

@interface MyBaseClass // << superclass omitted
@property (nonatomic, readwrite, retain) NSObject *someObject;
@end

NSObject (or a subclass of, assuming you're target apple's systems) should be the base class unless you're very experienced and know what a root class is for.

class extensions are often used to 'simulate' private interfaces. by simulate, the compiler doesn't enforce this, as it would be enforced in other languages. for example, all messages are still dynamic, although the subclass may (unknowingly) override methods in your extensions, if declared with the same selector.

justin
  • 104,054
  • 14
  • 179
  • 226
  • 2
    Following good encapsulation design, I am declaring the `someObject` property private because it should not be accessible from outside the class hierarchy. Only MyBaseClass and any subclasses of MyBaseClass should see that property. If I declare the property in the interface, aren't I making it a public property? – memmons Apr 07 '11 at 23:43
  • 1
    that is correct. the language offers no control over method visibility - only ivars. as mentioned, dynamic dispatch also allows subclasses to override any method, whether they know it or not. it's a pain sometimes, but a limitation of the language. you can tackle it a few ways: 1) use a c++ implementation/ivar, which probably offers all the visibility features you'll want. 2) document (or include in the method name) that the method is protected 3) add support for public usage 4) move the implementation to a protected ivar – justin Apr 08 '11 at 00:02
  • @Harkonian - Why should one need to be forcibly restrained from breaking encapsulation? You know it's a bad idea, so just don't do it - even if the language allows it. – Sherm Pendley Apr 08 '11 at 10:57
  • 2
    Sherm -- I'm all for good design, but if the language doesn't support it, what option do you have? It seems I can either not use properties at all, since they don't support scoping of any sort, and use ivars instead or I can use public properties for everything that needs to be visible in subclasses and document how they should be used. Either case breaks object-oriented design principles in some way. – memmons Apr 08 '11 at 16:08
  • 1
    @Sherm i write a bit in c++ as well as objc. it's too often that i'll attempt to break it - by accident. fortunately, the compiler checks and catches that. it's also nice to force an implementation to change, if something must be hidden. dependencies must be updated immediately in that event. programs live and are maintained by many people for many years, it's better to have a tool and language features to verify a design's correctness as programs evolve, imo. – justin Apr 08 '11 at 17:06
  • @Harkonian - What option do you have? Design your code well anyway. Just because the language doesn't *force* you to use good design doesn't mean it won't *allow* you to use it. – Sherm Pendley Apr 08 '11 at 17:15
  • 1
    @Justin - That's a good point, the lack of access restrictions does place a bigger burden on the programmer. With great power comes great responsibility! :-) – Sherm Pendley Apr 08 '11 at 17:16
  • @Justin: Suggestion 1) (only use ivars, not properties) seems to be quite limiting when dealing with OOD. Suggestion 2) is an option, but oviously not ideal since accidental use of public properties that are supposed to be private could easily happen. Suggestion 3) breaks encapsulation. Suggestion 4) Changing the scope of the ivar to protected seems the best route, even if the property is still available publicly. I'll have to do a bit of research to see how scoped ivars affect public properties. – memmons Apr 10 '11 at 17:15
  • @Harkonian re #1: the implementation and storage just moves to a c++ object. the c++ object then holds one or more ivars, declares the public interface, and is responsible for the implementation. then you just expose the interface the objc type offers by creating method wrappers. of the options i listed, it is the one i use it most... but what limitations are you concerned about? – justin Apr 10 '11 at 17:48
  • Well, this sucks. Why does ObjC not do it the way @Answerbot is saying? – olivaresF Jan 20 '12 at 02:05
4

Judging by the () after your base class name, it looks like you are declaring a private interface extension within your class implementation, is this the case? If so the variable will only be accessible from within that class implementation.

Does your MyBaseClass inherits from NSObject directly?

If so, you need to declare the someObject property in your interface file, as in:

@interface MyBaseClass : NSObject
{
}
@property (nonatomic, retain) NSObject *someObject;

And then synthesize it like you are already doing.

Rog
  • 18,602
  • 6
  • 76
  • 97
  • You and Justin make a similar point -- and I do think that is the answer. I just need to understand how this design works when considering encapsulation. Please see my comment to Justin's answer. – memmons Apr 07 '11 at 23:45
  • Good point! You can declare your iVars as private with `@private`. More details here http://stackoverflow.com/questions/844658/what-does-private-mean-in-objective-c – Rog Apr 07 '11 at 23:48
  • Thanks for the reference, Rog. It definitely clears up the scope of ivars. It doesn't mention properties, though and actually confuses the issue for me. If I have an ivar declared protected, is the property that uses that ivar protected as well? – memmons Apr 07 '11 at 23:53
  • This SO discussion has some additional pertinent information: http://stackoverflow.com/questions/1338434/what-is-the-objective-c-equivalent-of-a-public-get-protected-set-property-in-c – memmons Apr 08 '11 at 00:01
  • Another SO discussion with relevance: http://stackoverflow.com/questions/4264263/objective-c-properties-for-class-and-its-subclasses-only – memmons Apr 08 '11 at 00:03
  • Aside from doing what you were doing initially (which as you know now has its limitations), I am not aware of a way for you to declare a @property as private. In this case I'd probably recommend that you generate your own setter/getter methods in the parent class instead. – Rog Apr 08 '11 at 00:07
  • The second link is a good idea - make it readonly public and redeclared it as readwrite privately. Here's Apple's take on it - hhttp://developer.apple.com/library/Mac/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html#//apple_ref/doc/uid/TP30001163-CH17-SW19 – Rog Apr 08 '11 at 00:08
  • 1
    @Harkonian all methods are publicly visible/callable via the runtime - all a client needs is selector's name, and in some cases its signature. some people simulate access reduction by declaring methods in implementation (extensions are one example). the compiler will warn you if a selector with a matching name/sig could not be found, or if the compiler can deduce that the object may not respond to the selector. even those warnings may be stepped around and the method may be called if a client is insistent. – justin Apr 08 '11 at 00:09
0

This is an alternative that meets most of the objectives.

In your header, define the interface

@interface MyBaseClass : NSObject {
    NSObject *inheritableObject;
}
@property (readonly) NSObject *inheritableObject;

Now you can edit the inheritableObject in MyBaseClass as well as in any Class that inherits from MyBaseClass. However, from the outside, it is readonly. Not private as in the case of @interface MyBaseClass(), but protected from uncontrolled changes.

Hans Bot
  • 1
  • 1
-3

super.someObject = nil;. Inheritance means MyBaseClass is your super class.

Alan Zeino
  • 4,406
  • 2
  • 23
  • 30
  • Tried that -- same error. In any case, even if this did work it woudl be very fragile. What if you have a deep hierarchy? super.super.super.super.someObject? – memmons Apr 07 '11 at 23:44