What is a good pattern for creating and updating a class with readonly properties? An example for such a class
#interface ReadOnlyClass : NSObject
#property (nonatomic, assign, readonly) NSUInteger number;
#end
It is not necessary for the class user to call the init constructor (although this is not really possible to avoid in Objective-C) or anything else. Apple uses this pattern e.g. in CMLogItem or CLLocation. So, how can I change/update the value of number in a good manner?
If I understood you correct, number is a computed property or – to have a better term – depending property. The value of number depends on other properties that can change themselves. Correct?
Usually you simply write an explicit setter for this:
-(NSUInteger)number
{
return /* your calculation */;
}
For example: You have another property playerName and you want to return the number of characters. (Okay, not very exiting, but I need an example.)
-(NSUInteger)number
{
return [self.playerName length];
}
Please note: Since you overwrite all methods declared for the read-only property, no ivar is synthesized.
If the calculation is expensive or the getter is executed more often than the setters for the changeable properties (playerName) by far, it can be an optimization to change the property inside the class into a read-write property and set the value, every time the changeable properties are changed.
#interface ReadOnlyClass ()
#property (readwrite, …) NSUInteger number;
…
#end
// number is synthesized in this case
-(void)setPlayerName:(NSString*)playerName
{
_playerName = playerName;
self.number = [playerName length];
}
I believe the best pattern matching for what ask is the Builder Pattern.
EG : You have your read only property which is the result, then some function calculate. You can set various parameters inside like
-(void) setMathExpression:(NSString) aExpression;
-(void) calculate;
Then the user can set an expression, call calculate then get the result from the readonly property.
Related
I'm trying to readwrite variables from an API class which variables are readonly
Is it possible to get their setters from those readonly variables?
Please note that I have no access to this classes since there are from a private API.
Here is one example:
Private APIAxes.h
#interface APIAxes
#property (nonatomic, readonly) double x;
#property (nonatomic, readonly) double y;
#property (nonatomic, readonly) double z;
#end
My AxesClass.m
#property (nonatomic) APIAxes *apiA;
- (void)updateAxes
{
APIAxes *temp = [APIAxes alloc];
temp.x = 0.1; //Error x variable is readonly
temp.y = 0.2; //Error y variable is readonly
temp.z = 0.3; //Error z variable is readonly
self.apiA = temp; // This works
}
If there's no initializer for this APIAxes class, and no setters, then it sounds like you really shouldn't try to do this. Granted that the class appears to be a pseudo-struct, a simple data object that can be put into NS collections, but absent a guarantee that an APIAxes instance won't change once it's been handed out to you, you stand a good chance of causing mysterious library-internal problems by monkeying with the values. If the library authors had accounted for this, they would have provided setters, or at the very least a constructor.
Now, having given the foot-shooting warning... You should be able to use Key-Value Coding to modify these values. I say "should" because, as Itai Ferber notes below, it relies on a particular naming convention, and it's possible to implement a class so that KVC won't work. But declared properties were implemented with KVC in mind, so if these are standard properties, you're all set.
KVC can change a property value even absent a setter: it will find the ivar itself if necessary via the ObjC runtime library. The only thing to note is that since these properties are primitives, you need to box the new values into NSNumbers first.
So [temp setValue:#(0.1) forKey:#"x"]; should do the trick.
Yeah because APIAxes defined the properties as readonly, your hands are tied. Generally what they'll give you is an initializer method that takes arguments that set those properties, but that doesn't appear to be the case?
What is difference of these three cases (all of them are used like private fields):
1.
#interface APLParseOperation : NSOperation
#property (copy, readonly) NSData *earthquakeData;
#end
2.
#interface APLParseOperation () <NSXMLParserDelegate>
#property (nonatomic) APLEarthquake *currentEarthquakeObject;
#property (nonatomic) NSMutableArray *currentParseBatch;
#property (nonatomic) NSMutableString *currentParsedCharacterData;
#end
3.
#implementation APLParseOperation
{
NSDateFormatter *_dateFormatter;
BOOL _accumulatingParsedCharacterData;
BOOL _didAbortParsing;
NSUInteger _parsedEarthquakesCounter;
}
It's a good practice or smthing else?
I'm going to go through each example you gave, and describe them. I was just having trouble with this yesterday so I feel your pain.
1.
#interface APLParseOperation : NSOperation
#property (copy, readonly) NSData *earthquakeData;
#end
By using the #property keyword, the compiler automatically synthesizes your accessor methods for you, and also a backing instance variable. However, because you are using the readonly property attribute, the compiler is only synthesizing a getter method for you.
2.
#interface APLParseOperation () <NSXMLParserDelegate>
#property (nonatomic) APLEarthquake *currentEarthquakeObject;
#property (nonatomic) NSMutableArray *currentParseBatch;
#property (nonatomic) NSMutableString *currentParsedCharacterData;
#end
This second example is very similar to the first. However, because none of them have the readonly property attribute, they will all have getters and setter methods synthesized for them, as well as the backing instance variable.
3.
#implementation APLParseOperation
{
NSDateFormatter *_dateFormatter;
BOOL _accumulatingParsedCharacterData;
BOOL _didAbortParsing;
NSUInteger _parsedEarthquakesCounter;
}
For this last example, you are just declaring instance variables. These are also private to your implementation file, where as the other 2 examples had declarations being made in your classes interface file.
No setter or getter methods are being synthesized for you by the compiler. You are simply declaring some instance variables.
In terms of private and public, your first and second examples both provide declarations that will be visible to other classes, as long as they import the current class's header file. The first example however, only provides a way to "get" the property and read it, there is no setter method because you used the readonly property attribute. With the second example, outside classes will be able to access your getter and setter methods for your property, so they can read and write.
For the third example, these are just instance variables and they are private to your class's implementation file. Basically, no outside classes will even know that they exist.
This is not private. It is still readable by outside classes, though it can't be written.
Private properties. It can be useful if you want to write custom getters and setters. If you are not using ARC, it can be helpful for memory management.
Private members. This is my favorite. It's easy to read and easy to write.
defines a public property visible to all users of the APLParseOperation class.
defines properties through an extension, making them available only to the implementation methods.
defines instance variables which are implicitly private.
Number 1 is used when you want to make your properties public. Numbers 2 and 3 are for private properties and instance variables. You can also declare instance variables in class extensions, like this:
#interface APLParseOperation () <NSXMLParserDelegate>
{
NSDateFormatter *_dateFormatter;
BOOL _accumulatingParsedCharacterData;
BOOL _didAbortParsing;
NSUInteger _parsedEarthquakesCounter;
}
#end
There is not much difference between that and the number 3. It is a good idea to pick one style, and stick to it in all your code.
Case 1. is not private. It's a public read-only property: Reading is public, writing is only possible only in the private scope via the underlying ivar (thanks for pointing it out #mah).
Case 2. (if in a .m file) is extending the class by adding 3 private properties and making the protocol conformance private too.
Case 3. is declaring 4 private instance variables that can be used in all the implementation scope.
Objective-C best practice for private properties is case 2., as case 1. is not private at all, just read-only, and case 3. uses instance variables (aka ivar) which is less conventional than properties. More on that here: Reason to use ivars vs properties in objective c
Hope this helps,
I have written a setter that is not working as expected. I am reading in statistics from a file and populating the properties of my SVTeam object with these stats. However some of the properties need to be calculated (as they are not supplied in the files). One of these properties holds the home winning percentage for a given team. I do not need to supply an argument for the setter. Two questions:
Since I'm NOT supplying an argument in my setter is it still truly a setter in an objective-c sense?
Even it if is not a true setter why does the code below still leave the properties as null?
SVTeam.h
#interface SVTeam : NSObject
#property (weak, nonatomic) NSNumber *homePercentage;
...
...
SVTeam.m
#import "SVTeam.h"
#implementation SVTeam
#synthesize homePercentage = _homePercentage;
...
...
-(void) setHomePercentage
{
float wins = [_homeWins floatValue];
float losses = [_homeLosses floatValue];
float ties = [_homeTies floatValue];
float winPercentage = wins / (wins+losses+ties);
self.homePercentage = [NSNumber numberWithFloat:winPercentage];
}
Since homePercentage is a calculated value based on other properties you could make it a readonly property and you would have your calculation logic in the getter instead. If you leave it as a setter then your consumers would have to make sure to call the setter first and call the getter to retrieve the value.
#property(nonatomic,readonly) NSNumber *homePercentage;
And your getter definition would be
- (NSNumber *)homePercentage
{
...
return [NSNumber numberWithFloat:winPercentage];
}
To expand on Garfield81's answer:
If you want to override the setter synthesized by clang for you homePercentage property, you need to implement -(void)setHomePercentage:(NSNumber *)homePercentage; in your class.
Apple documentation on properties.
To answer your two questions:
1) since I'm NOT supplying an argument in my setter is it still truly a setter in an objective-c sense?
No. The synthesized setters takes an unique argument of the same type of the propery and are named set<CapitalizedPropertyName>. What you did is declare a new method.
2) even it if is not a true setter why does the code below still leave the properties as null
There is not apparent reason for homePercentage to stay nil (not null) from the code you provided. Are you sure your custom method gets called? (remember that when using the dot notation (self.homePercentage), this is -(void)setHomePercentage:(NSNumber *)homePercentage; which is called, not your -(void)setHomePercentage;).
Note also that you don't need to add #synthesize homePercentage = _homePercentage; unless you override both the getter and the setter.
In my implementation I have getters and setters like below. I want to use properties and synthesize the getters and setters but have a few questions.
- (NSString *)title {
return title;
}
- (void)setTitle:(NSString *)value {
if(title != value) {
[title release];
title = [value retain];
}
}
If I was to convert that to a property, what attributes would I use? Am I right in thinking:
readwrite so both getters and setters are present
retain so that it increase the retain value of the value string so the object don't lose it.
Am I right with the above?
One final thing. I have the method below ...
- (void)setReleaseDate:(NSString *)value {
// YYYY-MM-DD HH:MM:SS +HHMM
if([releaseDate description] != value) {
[releaseDate release];
releaseDate = [[NSDate alloc] initWithString:value];
}
}
Am I right in thinking I still have to include that method because it contains code that the synthesized getter would not include?
Thanks.
For your title property, you can declare it in your class interface as follows:
#property (nonatomic, retain) NSString* title;
Which is the same as the following:
#property (readwrite, nonatomic) NSString* title;
readwrite is a default setting. Most of the time you will want setters for your properties, so for the times when you don't you would use the non-default readonly to specify this.
The nonatomic part basically means that the accessors will be faster, and is typically used. You can find out more information about this here: What does the property "Nonatomic" mean?.
For your second question, you can implement your own accessors if you wish. If you do this then it kind of 'overrides' the accessor that would be generated by Objective-C. Remember that you have to keep to the naming conventions. So in your example, the "setReleaseDate:" method you've defined would be used for the setter method for the property "releaseDate" - which is completely correct! :) The problem you have though is that you're passing an *NSString** to set the date, which means that this method won't override the default setter that would be used if you synthesized the property. You have to pass a value of the same type as the one you're setting as the single argument, so for this case you would have to pass an *NSDate**.
You must also ensure that if you provide your own implementation of an accessor that it does what is declared in the interface. I presume your releaseDate property should be declared as retain.
Your assertion about using readwrite as well as retain is correct as it would create semantically equivalent code to what you have posted.
The releasedate property setter can't be synthesized as you're transforming a NSString into a NSDate to store it, that also avoids common issues with NSString properties, for which you'd better use copy to avoid problems with NSMutableString.
Other than that, your code is fine, except that for string comparison you may want to replace the simple pointer check != with isEqualToString, see Comparing Strings in Cocoa.
It is common, though not always required, to use copy semantics for NSString properties, to avoid issues with NSMutableString objects being changed behind your back.
Otherwise, you seem to be pretty much on top of it.
Can custom property attributes be created in Objective-C just like in VB.NET? For example, in VB.NET you can create the "Browsable" attribute and read it at runtime to determine whether you should display a property or not.
Public Class Employee
<Browsable(True)> _
Public Property Property1() As String
Get
End Get
Set(ByVal Value As String)
End Set
End Property
<Browsable(False)> _
Public Property Property2() As String
Get
End Get
Set(ByVal Value As String)
End Set
End Property
End Class
I would like to do the same in Objective-C, even if it is a fixed attribute that can only be set at compile time and cannot be changed at all.
What I'm trying to do is to add an attribute to properties of my class to determine whether the properties should be serialized or not.
I know the standard Objective-C attributes (readonly, nonatomic, etc.), but those don't help me... unless you have a creative way of using them. I also looked into using C attributes with the __attribute__(("Insert attribute here")) keyword, but C has specific attributes that serve specific purposes, and I'm not even sure you can read them at runtime. If I missed one that can help me, let me know.
I tried using typdef. For example:
typdef int serializableInt;
serializableInt myInt;
and use the property_getAttributes() Objective-C runtime function, but all it tells me is that myInt is an int. I guess typedef is pretty much like a macro in this case... unless I can create a variable of type serializableInt at runtime. Anyhow, here's Apple's documentation on the values you get from property_getAttributes().
The other requirement is that this attribute has to work with NSObject sub-classes as well as primitive data types. I thought about the idea of adding to the class a black lists or white lists as an ivar that would tell me which properties to skip or serialize, which is basically the same idea. I'm just trying to move that black/white list to attributes so it's easy to understand when you see the header file of a class, it's consistent across any class I create and it's less error prone.
Also, this is something to consider. I don't really need the attribue to have a value (TRUE or FALSE; 1, 2, 3; or whatever) because the attribute itself is the value. If the attribute exists, then serialize; otherwise, skip.
Any help is appreciated. If you know for sure that this is not possible on Objective-C, then let me know. Thanks.
If you want to add attribute to property, class, method or ivar, you can try to use github.com/libObjCAttr. It's really easy to use, add it via cocoapods, and then you can add attribute like that:
#interface Foo
RF_ATTRIBUTE(YourAttributeClass, property1 = value1)
#property id bar;
#end
And in the code:
YourAttributeClass *attribute = [NSDate RF_attributeForProperty:#"bar" withAttributeType:[YourAttributeClass class]];
// Do whatever you want with attribute, nil if no attribute with specified class
NSLog(#"%#", attribute.property1)
unless i've missed your point…
i'd recommend declaring a protocol. then using instances of objc objects as variables in your objc classes which adopt the protocol.
#interface MONProtocol
- (BOOL)isSerializable;
- (BOOL)isBrowsable;
/* ... */
#end
#interface MONInteger : NSObject <MONProtocol>
{
int value;
}
- (id)initWithInt:(int)anInt;
#end
#interface MONIntegerWithDynamicProperties : NSObject <MONProtocol>
{
int value;
BOOL isSerializable;
BOOL isBrowsable;
}
- (id)initWithInt:(int)anInt isSerializable:(BOOL)isSerializable isBrowsable:(BOOL)isBrowsable;
#end
// finally, a usage
#interface MONObjectWithProperties : NSObject
{
MONInteger * ivarOne;
MONIntegerWithDynamicProperties * ivarTwo;
}
#end
if you want to share some implementation, then just subclass NSObject and extend the base class.
you'd then have a few variants to write for the types/structures you want to represent.
The deficiency with the other answers I've seen so far is that they are implemented as instance methods, i.e., you need to have an instance already before you can query this metadata. There are probably edge cases where that's appropriate, but metadata about classes should be implemented as class methods, just as Apple does, e.g.:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString*)key { }
We could imagine our own along similar lines:
+ (BOOL)keyIsBrowsable:(NSString*)key { }
or
+ (NSArray*)serializableProperties { }
Let's imagine our class is called FOOBar, and we want to know whether the baz key is browsable. Without having to create a FOOBar we can just say:
if ([FOOBar keyIsBrowsable:#"baz"]} { ... }
You can do pretty much anything with this technique that can be done with custom attributes. (Except for things like the Serializable attribute which require cooperation from the compiler, IIRC.) The nice thing about custom attributes, though, is that it is easy to distinguish at a glance what is metadata and what is intrinsic to that class's actual functionality, but I think that's a minor gain.
(Of course, you may have to check for the existence of the keyIsBrowsable: selector, just as you'd have to check for the existence of a specific custom attribute. Again, custom attributes have a slight leg up here, since we can tell the .NET runtime to give them all to us.)
I've come across a similar issue whe serializing objects. My solution is to add a #property (nonatomic, readonly) NSArray *serialProperties; which has a custom getter that returns the names (as NSString*) of the properties of this (sub-)class that should be serialized.
For example:
- (NSArray *)serialProperties {
return #[#"id", #"lastModified", #"version", #"uid"];
}
Or in a subclass:
- (NSArray *)serialProperties {
NSMutableArray *sp = [super serialProperties].mutableCopy;
[sp addObject:#"visibleName"];
return sp;
}
You can then easily get all properties and their values via [self dictionaryWithValuesForKeys:self.serialProperties].
You can't add custom properties other than what sdk has provided..
.
But there is a work around to attain your objective...
#interface classTest:NSObject
#property(strong,nonatomic)NSString *firstName;
#property(strong,nonatomic)NSString *lastName;
#property(strong,nonatomic)NSMutableDictionary *metaData;
#end
#implementation classTest
- (id) init
{
self = [super init];
//Add meta data
metaData=[[NSmutableDictionary alloc]init];
//
if( !self ) return nil;
return self;
}
#end
so use the dictionary to add and retrieve meta data...
i hope it helps....