Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the compiler warn when redeclaring base class's readwrite property as readonly in subclass?

The Apple doc quoted later seems to indicate this is permissible, though I'll admit never having a reason to do it in a subclass until now.

I have a base class with a public readwrite property and a subclass where I redeclare the property as readonly. The subclass also has a class extension which again redeclares the property as readwrite to achieve the common "public readonly, private readwrite" Objective-C pattern. However, I get the following compiler warning:

warning: Semantic Issue: Attribute 'readonly' of property 'foo' restricts attribute 'readwrite' of property inherited from 'Base'

I'm using Xcode 4.1 build 4B110 with LLVM 2.1 (though LLVM GCC4.2 and GCC4.2 give the same warning) on 10.7.

Here's a stripped-down example which exhibits the compiler warning:

#import <Foundation/Foundation.h>

@interface Base : NSObject
@property (nonatomic, readwrite) BOOL foo;
@end

@implementation Base
@dynamic foo;
@end

// Subclass
@interface Sub : Base
@property (nonatomic, readonly) BOOL foo;
@end

// Class extension 
@interface Sub ()
@property (nonatomic, readwrite) BOOL foo;
@end

@implementation Sub
@dynamic foo;  // it warns with @synthesize as well
@end

Here's a relevant passage from Apple's The Objective-C Programming Language:

Property Redeclaration

You can redeclare a property in a subclass, but (with the exception of readonly versus readwrite) you must repeat its attributes in whole in the subclasses. The same holds true for a property declared in a category or protocol—while the property may be redeclared in a category or protocol, the property’s attributes must be repeated in whole.

If you declare a property in one class as readonly, you can redeclare it as readwrite in a class extension (see “Extensions”), in a protocol, or in a subclass (see “Subclassing with Properties”). In the case of a class extension redeclaration, the fact that the property was redeclared prior to any @synthesize statement causes the setter to be synthesized. The ability to redeclare a read-only property as read/write enables two common implementation patterns: a mutable subclass of an immutable class (NSString, NSArray, and NSDictionary are all examples) and a property that has a public API that is readonly but a private readwrite implementation internal to the class. The following example shows using a class extension to provide a property that is declared as read-only in the public header but is redeclared privately as read/write.

I redeclare public readonly properties readwrite in class extensions all the time, but I guess I've never had cause to do it an a subclass. However, unless I'm reading it wrong, the paragraphs above seem to indicate that it's kosher. Can anyone set me straight and/or reconcile the apparent conflict between the docs and the compiler?

Why do I want to do this? My real-world situation is more complex, of course. I can make design changes to work around this if needed, but this seemed like the least-friction alternative (the need to do this at all is being driven by other changes).

like image 470
Slipknot Avatar asked Aug 22 '11 01:08

Slipknot


1 Answers

It says you can redeclare a readonly property as readwrite but you're doing the opposite. You can't/shouldn't do it because it's possible to do this:

Sub* s = [[[Sub alloc] init] autorelease];
Base* b = s; 
b.foo = YES; //legal for `Base` objects, but not legal for `Sub` objects

It's a violation of the the Liskov Substitution Priciple.

like image 72
Tom Dalling Avatar answered Dec 03 '22 07:12

Tom Dalling