The Objective-C runtime keeps a list of declared properties as meta-data with a Class object. The meta-data includes property name, type, and attributes. The runtime library also provides a couple of functions to retrieve these information. It means a declared property is more than a pair of accessor methods (getter/setter). My first question is: Why we (or the runtime) need the meta-data?
As is well known, a declared property cannot be overridden in subclasses (except readwrite vs. readonly). But I have a scenario that guarantees that needs:
#interface MyClass : MySuperClass <NSCopying, NSMutableCopying>
#property (nonatomic, copy, readonly) NSString *string;
- (id)initWithString:(NSString *)aString;
#end
#interface MyMutableClass : MyClass
#property (nonatomic, strong, readwrite) NSMutableString *string;
- (id)initWithString:(NSString *)aString;
#end
Of course, the compiler won't let the above code pass through. My solution is to substitute the declared property with a pair of accessor methods (with the readonly case, just the getter):
#interface MyClass : MySuperClass <NSCopying, NSMutableCopying> {
NSString *_string;
}
- (id)initWithString:(NSString *)aString;
- (NSString *)string;
#end
#implementation MyClass
- (id)initWithString:(NSString *)aString {
self = [super init...];
if (self) {
_string = [aString copy];
}
return self;
}
- (NSString *)string {
return _string;
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
return [[MyMutableClass alloc] initWithString:self.string];
}
#end
#interface MyMutableClass : MyClass
- (id)initWithString:(NSString *)aString;
- (NSMutableString *)string;
- (void)setString:(NSMutableString *)aMutableString;
- (void)didMutateString;
#end
#implementation MyMutableClass
- (id)initWithString:(NSString *)aString {
self = [super init...];
if (self) {
_string = [aString mutableCopy];
}
return self;
}
- (NSMutableString *)string {
return (NSMutableString *)_string;
}
- (void)setString:(NSMutableString *)aMutableString {
_string = aMutableString;
// Inform other parts that `string` has been changed (as a whole).
// ...
}
- (void)didMutateString {
// The content of `string` has been changed through the interface of
// NSMutableString, beneath the accessor method.
// ...
}
- (id)copyWithZone:(NSZone *)zone {
return [[MyClass alloc] initWithString:self.string];
}
#end
Property string needs to be mutable because it is modified incrementally and potentially frequently. I know the constraint that methods with the same selector should share the same return and parameter types. But I think the above solution is appropriate both semantically and technically. For the semantic aspect, a mutable object is a immutable object. For the technical aspect, the compiler encodes all objects as id's. My second question is: Does the above solution make sense? Or it's just odd?
I can also take a hybrid approach, as follows:
#interface MyClass : MySuperClass <NSCopying, NSMutableCopying> {
NSString *_string;
}
#property (nonatomic, copy, readonly) NSString *string;
- (id)initWithString:(NSString *)aString;
#end
#interface MyMutableClass: MyClass
- (id)initWithString:(NSString *)aString;
- (NSMutableString *)string;
- (void)setString:(NSMutableString *)aMutableString;
- (void)didMutateString;
#end
However, when I access the property using the dot syntax like myMutableObject.string, the compiler warns that the return type of the accessor method does not match the type of the declared property. It's OK to use the message form as [myMutableObject string]. That suggests another aspect where a declared property is more than a pair of accessor methods, that is, more static type checking, although it is undesirable here. My third question is: Is it common to use getter/setter pair instead of declared property when it is intended to be overridden in subclasses?
My take on this would be slightly different. In the case of the #interface of an Objective-C class, you are declaring the API that class uses with all classes that communicate with it. By replacing the NSString* copy property with an NSMutableString* strong property, you are creating a situation where unexpected side-effects are likely to occur.
In particular, an NSString* copy property is expected to return an immutable object, which would be safe for using in many situations that an NSMutableString* object would not be (keys in dictionaries, element names in an NSXMLElement). As such, you really don't want to replace these in this fashion.
If you need an underlying NSMutableString, I would suggest the following:
Add an NSMutableString* property in addition to the string property, and name it -mutableString
Override the -setString: method to create an NSMutableString and store it
Override the -string method to return an immutable copy of your mutable string
Carefully evaluate whether you can replace the internal ivar with an NSMutableString or not. This might be a problem if you don't have access to the original class and you aren't certain whether assumptions are made about the mutability of the string inside of the class
If you do this, you will maintain the current interface without disrupting existing users of the class, while extending the behavior to accommodate your new paradigm.
In the case of changing between a mutable object and an immutable one, you really need to be careful that you don't break the API contract for the object.
Related
I'm try to grok properties declared as both copy and readonly in objective-c, and specifically, whether I have to do the copy myself. In my init methods. Evidence suggests I do:
#interface A : NSObject
#property(nonatomic, copy, readonly) NSData *test;
- (instancetype)initWithData:(NSData *)data;
#end
#implementation A
- (instancetype)initWithData:(NSData *)data {
if ((self = [super init]) != nil) {
_test = data;
}
return self;
}
#end
int main (void) {
NSData *d1 = [NSMutableData dataWithBytes:"1234" length:5];
A *a = [[A alloc] initWithData:d1];
NSLog(#"%lx", (unsigned long)d1);
NSLog(#"%lx", (unsigned long)a.test);
return 0;
}
I had thought I could do self.test = data in my init method, but that is not permitted because it's readonly (not unexpectedly). Of course, self.test = [data copy] ensures two different objects.
So: Is there a way to create a readonly property in objective-c that copies the incoming value, or is it sufficiently an edge case that the combination is pointless and I have to do any copying myself manually anyway?
A #property declaration is merely shorthand for some accessor/mutator method declarations, and (in some cases) synthesized implementations for said accessor/mutator methods.
In your case, the #property(nonatomic, copy, readonly) NSData *test declaration expands to this equivalent code:
#interface A : NSObject
{
NSData* _test;
}
- (NSData*)test;
#end
#implementation A
- (NSData*)test
{
return _test;
}
#end
There is no setTest: mutator method because the property is declared as readonly, so the copy attribute has no effect.
You can implement your own mutator method:
- (void)setTest:(NSData*)newValue
{
_test = [newValue copy];
}
Or, you can have the compiler synthesize a mutator method for you by declaring a read/write property in a private class extension in your implementation file:
// A.m:
#interface A()
#property (nonatomic, copy) NSData* test;
#end
Both cases would allow you to use the test mutator method to copy a value to the _test instance variable:
- (instancetype)initWithData:(NSData *)data {
if ((self = [super init]) != nil) {
self.test = data;
}
return self;
}
The end result is:
#interface A : NSObject
#property(nonatomic, copy, readonly) NSData* test;
- (instancetype)initWithData:(NSData*)data;
#end
#interface A()
#property (nonatomic, copy) NSData* test;
#end
#implementation A
- (instancetype)initWithData:(NSData*)data {
if ((self = [super init]) != nil) {
self.test = data;
}
return self;
}
#end
In addition to what Darren said, the copy attribute describes what semantics the properties setter has. In your initializer, you're not using the setter, you're directly assigning to the instance variable.
It's maybe a bit hard to grok, but the instance variable is not the same thing as the property. It is used to implement the property in this case. But, assigning to the instance variable is not the same as setting the property.
If you want your initializer to also have the semantics that it copies the passed-in data, that's a separate design decision (although a good idea to go with the property's semantics). You could implement that by using a private setter as Darren suggests, but you could also just do:
_test = [data copy];
in the initializer.
I have an academic question about Class Method exposure. There is something that I obviously don't understand about this and would like some clarification from those in the know.
Background:
I have a simple example of two classes named ViewController and ClassB. Class B contains an array with a method named returnArray. The ViewController accesses the array's data. I have exposed the returnArray method in the ClassB.h file.
Question:
Why is it that I can access the array's data in ViewController without having to define a property? I thought that the property would create a getter to allow access to the array. My example (only exposing the method) allows me to access the data without the creation of the #property.
Class Method:
ClassB.h
#interface ClassB : UIViewController
+(NSArray *) returnArray;
//#property (nonatomic, strong) NSArray *returnArray;
ClassB.m
#implementation ClassB
+(NSArray *) returnArray
{
NSArray *locationArray = #[#"Place1", #"Place2"];
return locationArray;
}
ViewController.m
- (void)viewDidLoad
{
NSArray *location = [ClassB returnArray];
NSLog (#"The count of the location is %d", [location count]);
NSLog (#"The second item in testArray is %#", location[1]);
}
Instance method: After reviewing answers
ClassB.h
*/
{
#private
NSArray *returnArray;
}
- (void)setReturnArray:(NSArray*)returnArray;
-(NSArray *) returnArray;
*/
#property (nonatomic, strong) NSArray *returnArray;
#end
ClassB.m - no change
ViewController.h - no change
ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
//Create instance of ClassB
ClassB *classB = [ClassB new];
//Access the instance of returnArray
NSArray *location = [classB returnArray];
NSLog (#"The count of the location is %d", [location count]);
NSLog (#"The second item in testArray is %#", location[1]);
}
#property is a shorthand notation for creating an instance variable and associated accessor methods (with defined access / modification criteria).
What you have is a class method, which internally creates an array and returns it.
That's why you call [ClassB returnArray]; instead of [instanceOfB array];.
These are completely different things. If you wanted to use a property then you would need to create an instance of ClassB and then access the property. This would work, assuming that the array was created when the instance of ClassB was created.
Wain's answer addresses the difference between #property and Class methods, so it's worth a read. My answer assumes you know the difference between class and instance methods, and focuses on the difference between creating a #property versus creating an instance variable with an associate setter and getter.
The reason is because returnArray is a public method that returns an NSArray object on your ClassB.
A #property is merely a convenient way of creating three things at the same time: an instance variable, a setter, and a getter. It has the added bonus of allowing dot-syntax.
But at the end of the day, dot-syntax aside, all you're doing by declaring a #property is equivalently equal to this:
#interface ClassB : NSObject {
#private
NSArray *returnArray;
}
- (void)setReturnArray:(NSArray*)returnArray;
- (NSArray*)returnArray;
This is the same as this:
#property NSArray *returnArray;
Except of course, the dot syntax.
When you do:
NSArray *myArray = classB.returnArray;
You're not actually directly accessing the array you created when you declared the #property.
What you're doing is calling the getter method that was automatically generated when you declared the #property.
Studying polymorphism in objective c . I'm trying to do some things using an NSMutableArray that is held by my super class, so I need a pointer to it from the sub class.
SuperViewController.h
#interface SuperViewController : UIViewController
- (NSMutableArray *) myArray;
#end
SuperViewController.m
#interface SuperViewController()
#property (strong, nonatomic) NSMutableArray *myArray;
#end
#implementation SuperViewController
- (NSMutableArray *) myArray {return self.myArray;}
#end
Then, in my subclass, I have this:
SubViewController.h
#import SuperViewController.h
#interface SubViewController : SuperViewController
- (void) updateUI;
#end
SubViewController.m
#implementation
- (void) updateUI
{
(NSMutableArray *) myArray = [super myArray];
}
#end
It crashes the App . I've tried quite a few different approaches, but I always wind up crashing. Can anyone help me out? Thanks!
The reason is, that you have a recursion which blows the stack:
- (NSMutableArray *) myArray
{
return self.myArray;
}
This method calls itself. The dot notation is a short form of method invocation:
- (NSMutableArray *) myArray
{
return [self myArray];
}
It is not an access to the object's context (ivar) as you probably expected:
- (NSMutableArray *) myArray
{
return self->_myArray;
}
The right way is to use the ivar directly:
- (NSMutableArray *) myArray
{
return _myArray;
}
BTW:
Properties can have the readonly attribute.
Using super in the derived class is useless and dangerous. It does not use the super's context (which does not exist, because every instance object has only one context built from the whole class hierarchy), but uses the super implementation of the method. You did not overwrite this implementation in the derived class. self would work, too, and gives you the ability to overwrite the getter.
You've written self.myArray to access an instance variable. The dot notation is a property access, and will invoke a property-like method even if not declared as a property. In your case this means your attempt to access the instance variable is in fact a self-recursive call, which will result in stack overflow.
To access an instance variable either use just it's name or the -> member access operator, e.g. _myArray or self->_myArray.
I need to enforce the initialization of an ivar in a superclass but that ivar usually can not be initialized without other data in the subclass to be initialized. The two solutions I have thought of is:
pass the required generated key for the ivar to the superclass's init method
calling a second superclass method from the subclass's init method
Here is example (contrived, non-working) code. The stringBasedOnSubclassKey ivar should be initialized to the NSString from the subclass's key method.
#interface MySuperclass : NSObject
#property (nonatomic, readonly) NSString *stringBasedOnSubclassKey;
#end
#interface MySubclass : MySuperclass
#property (nonatomic, assign, readonly) int value;
#end
#implementation MySubclass
- (instancetype)init
{
if (self = [super init]) {
_value = 30;
}
return self;
}
- (NSString *)key
{
return [NSString stringWithFormat:#"UniqueKey-%d", self.value];
}
So the question is is there a way to enforce the initialization of the stringBasedOnSubclassKey ivar using the return value of the "key" method? I don't believe I can enforce solution 1 and 2 above. These subclasses may also be created by other outside developers so the key method may be more complicated than this.
Update: I am dealing with existing subclasses of this base class so solutions limiting the changes to existing subclasses is a factor.
Write the getter for stringBasedOnSubclassKey in such a way as to force initialization of it:
- (NSString *) stringBasedOnSubclassKey {
if !(_stringBasedOnSubclassKey) {
_stringBasedOnSubclassKey = // whatever;
}
return _stringBasedOnSubclassKey;
}
And write the superclass key method to throw an exception, thus forcing the client to override it in the subclass.
What it says on the tin: I'd like to use the #property/#synthesize syntax to define a property on my Objective-C 2.0 class, but I want to place restrictions on the range of values allowed in the property. For example:
#interface MyClass : NSObject {
int myValue;
}
#property (nonatomic) int myValue;
Implementation:
#implementation MyClass
#synthesize myValue(test='value >= 0');
Note that the syntax here is just an example. Is this, or something much like it possible? Alternately, what is the literal equivalent of a synthesized setter, so that I can ensure that I use the same object retention rules in my manual setters as is used in a synthesized one.
Assuming your properties are Key-Value compliant (as they would be if you are using #synthesize) you should also implement Key-Value compliant validators. Take a look at Apple's documentation on the matter: http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueCoding/Concepts/Validation.html
The important thing to note is that validation does not happen automatically except when using certain kinds of binding. You either call the validator directly or by calling validateValue:forKey:error:.
You could override the produced setter to call the validator before saving it but if you are using bindings this is probably not what you want to do as the validator will possibly be called more than once for a single modification.
Also note that the validator might change the value being validated.
So lets look at your example (untested, btw. I'm not near a Mac):
#implementation MyClass
#synthesize myValue;
-(BOOL)validateMyValue:(id *)ioValue error:(NSError **)outError
{
if (*ioValue == nil) {
// trap this in setNilValueForKey
// alternative might be to create new NSNumber with value 0 here
return YES;
}
if ( [*ioValue intValue] < 0 ) {
NSString *errorString = #"myValue must be greater than zero";
NSDictionary *userInfoDict = [NSDictionary dictionaryWithObject:errorString
forKey:NSLocalizedDescriptionKey];
NSError *error = [[[NSError alloc] initWithDomain:#"MyValueError"
code:0
userInfo:userInfoDict] autorelease];
*outError = error;
return NO;
} else {
return YES;
}
}
If you wanted to override the synthesised setter and make it do the validation (still untested):
- (void)setMyValue:(int)value {
id newValue = [NSNumber numberWithInt:value];
NSError *errorInfo = nil;
if ( [self validateMyValue:&newValue error:&errorInfo] ) {
myValue = [newValue intValue];
}
}
You can see we had to wrap the integer in an NSNumber instance to do this.
When you use the #synthesize the accessor methods are generated. You can implement your own which will overwrite the generated one.
You can put your own implementation inside the accessor methods, e.g. you can add value checking before assignment and so on.
You can ommit one or the other or both, the ones that you don't implement will be generated because of #synthesize, if you use #dynamic you are specifying that you will provide accessors either at compile or run time.
Accessors will have names derived from the property name myproperty and setMyproperty. The method signatures are standard so it is easy to implement your own. The actual implementation depends on property definition (copy, retain, assign) and if it is read-only or not (read-only doesn't get set accessor). For more details see objective-c reference.
Apple reference:
#synthesize You use the #synthesize
keyword to tell the compiler that it
should synthesize the setter and/or
getter methods for the property if you
do not supply them within the
#implementation block.
#interface MyClass : NSObject
{
NSString *value;
}
#property(copy, readwrite) NSString *value;
#end
#implementation MyClass
#synthesize value;
- (NSString *)value {
return value;
}
- (void)setValue:(NSString *)newValue {
if (newValue != value) {
value = [newValue copy];
}
}
#end