Most efficient way to have a publicly immutable, read-only array property that is privately mutable in Objective-C? [duplicate] - objective-c

This question already has answers here:
How to declare an immutable property backed by a mutable type?
(3 answers)
Closed 8 years ago.
I recently got into a minor (but not unfriendly) disagreement in the comments on this question about how best to implement a read-only, immutable array property with underlying mutable storage in Objective-C. (The comments have since been moved to chat.)
First, let me be clear about what I want.
I want a property that is read-only, i.e., it is not assignable. myObject.array = anotherArray should fail.
I want the type of that property to be immutable so that new elements cannot be added to it through the property itself. Clearly, this means the type of the property should be NSArray.
I want the storage (i.e., the ivar) for that property to be mutable, because I will provide methods on the containing class to mutate it.
Since it seems not to be clear to some, let me stress that this question is about the frogs property of FrogBox class below, nothing more and nothing less.
I'll use the same contrived example from the linked question, a box of frogs, called FrogBox:
#interface FrogBox : NSObject
#property (nonatomic, readonly) NSArray *frogs;
- (void)addFrog:(Frog*)frog;
- (void)removeFrog:(Frog*)frog;
#end
#implementation FrogBox {
NSMutableArray *_frogs;
}
#dynamic frogs;
- (instancetype)init {
self = [super init];
if (self) {
_frogs = [NSMutableArray array];
}
return self;
}
- (NSArray*)frogs {
return _frogs;
}
- (void)addFrog:(Frog*)frog {
[_frogs addObject:frog];
}
- (void)removeFrog:(Frog*)frog {
// Frog implements isEqual and hash.
[_frogs removeObject:frog];
}
#end
Now, let's get something out of the way. The #dynamic directive is not strictly necessary here. Using #dynamic suppresses automatic synthesis of an ivar for the frogs property. Of course, if automatic synthesis sees an ivar with the same name as what it would have created, it just uses the supplied one. So why do I use it? I think it adds clarity and signals intent. If you don't like it, just imagine it's not there. It's not germane to the question.
The question is not about whether it's a good idea to want a publicly readonly, immutable and privately mutable property or not. It's about whether this is the most efficient way (in terms of syntax and clock cycles) to achieve that goal. I believe that it is, but I'd like to hear from the community, and am perfectly open to having my mind changed.

I want a property that is read-only, i.e., it is not assignable. myObject.array = anotherArray should fail.
I want the type of that property to be immutable so that new elements cannot be added to it through the property itself. Clearly, this means the type of the property should be NSArray.
I want the storage (i.e., the ivar) for that property to be mutable, because I will provide methods on the containing class to mutate it.
Well, I'm going to give the same answer I gave in the comments on the other question. What I do is:
Declare the property a readonly copy NSArray property in the public interface. This takes care of your 1 and 2; this thing now cannot be written to and it's an immutable array.
Redeclare the property readwrite privately in the .m file. Now I have to right to assign to it from within this class. To implement a mutating method, I read with mutableCopy, call an NSMutableArray method, and set - thus getting an immutable NSArray again automatically. Despite the use of the word "copy" there is actually no overhead.

Related

Naming of formal parameters in setters with synthesized properties [duplicate]

This question already has answers here:
Good practice for disambiguating argument names versus instance variable names in Objective-C
(3 answers)
Closed 9 years ago.
I have been learning and using Objective-C for quite some time now (it also kind of was my first OOP language) and I finally would like to know how to correctly name synthesized properties.
Let's take the following scenario:
I have got a property called someVariable.
#property (nonatomic, retain) NSString *someVariable;
and synthesize it
#synthesize someVariable;
How would the custom setter look like conventionally ?
1)
I would go ahead and say something like
-(void)setSomeVariable:(NSString *)someVar{
//input parameter MAY sound/look foreign due to the difference to the property
someVariable = someVar;
}
2) (illegal)
But I would like to name the formal parameter just like the property for the sake of readability and convenience. More like in Java like this:
-(void)setSomeVariable:(NSString *)someVariable{
//obviously illegal because this would call the setter over and over again
self.someVariable = someVariable;
}
3) (unconventional)
and according to what I have been reading in the past this
#synthesize someVariable = _someVariable;
is said to be unconventional and not supposed to be used.
So, am I correct in concluding that the way I have been doing it until now, is the only way to create a custom setter ?
3) is not unconventional, it's exactly what the compiler does if you don't provide the #synthesize statement.
This means that, without the #synthesize statement and the ivar declaration, you have an implicit ivar named _someVariable, and a custom setter would usually have a parameter named someVariable
-(void)setSomeVariable:(NSString *)someVariable {
_someVariable = someVariable;
}
Also note that providing custom setter and getter methods for a particular property indicates to the Xcode compiler to not provide the implicit ivar (here _someVariable). In the case of readonly properties, the same if true if you provide just the getter method.
WWDC 2012 session 405 provides a lot of details around Objective-C constructs for modern versions of the compiler.
EDIT
As H2CO3 has suggested in his answer, the code I wrote assumes you're using ARC. If you are using MRC, the setter method would rather be :
-(void)setSomeVariable:(NSString *)someVariable {
[someVariable retain];
[_someVariable release];
_someVariable = someVariable;
}

How to prevent a variable from being created for a non-physical property in Objective-C? [duplicate]

This question already has answers here:
Declaring a property with no instance variable
(2 answers)
Closed 9 years ago.
In modern versions of Xcode, variables are automatically created to back properties. For example, #property (nonatomic, assign) BOOL isOpen would automatically create BOOL _isOpen. Is there a way to prevent such variables from being created when the property is meant to be non-physical? In the following example of a non-physical isOpen property, _isOpen is not needed. It is actually detrimental, because I've had co-workers inadvertdently use _isOpen and wonder why nothing would happen.
- (void) setIsOpen:(BOOL)isOpen
{
if (isOpen) {
[self.specialView open]
} else {
[self.specialView close];
}
}
- (BOOL) isOpen
{
return self.specialView.alpha > 0.0;
}
If you implement both the setter & the getter the variable will not be created. From Apple's docs:
The compiler will automatically synthesize an instance variable in all situations where it’s also synthesizing at least one accessor method. If you implement both a getter and a setter for a readwrite property, or a getter for a readonly property, the compiler will assume that you are taking control over the property implementation and won’t synthesize an instance variable automatically.
Your co-workers should not be able to reference _isOpen at all. I've checked your code in Xcode 4.6.3 and it behaves as per spec - no variable is created.
You need to specify both the getter and the setter. Assuming you do that, no instance variable is created.
Note that you can specify a different name for the getter, too. For boolean properties, the is prefix is usually dropped, though in the case of "open" I might leave it. I think it's clearer with the prefix.
For other boolean properties, though, something like this might be preferred:
#property (nonatomic, assign, getter=isOpen) BOOL open;
Then:
implement setOpen and isOpen.
read using BOOL value = object.open or BOOL value = [object isOpen]
write using object.open = value or [object setOpen:value].
Using #dynamic is not necessary; the recent compiler will detect you've specified both the getter and setter. #synthesize should not be necessary either. (There are a few edge cases; if you run into a case where it is necessary, by all means use it there).
Note, however, that I'm talking about the modern runtime here. 32-bit OSX targets use the legacy runtime, which has different rules.

Objective C - changing a local variable w/ setter and w/o setter [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Difference between self.ivar and ivar?
In Objective-C, what's the difference between [self setVariable: newStuff] and variable = newStuff?
When you have a class with a variable
#property (nonatomic) NSInteger num;
and you want to change the variable, typically you can do
[self setNum: newNum]
but you can also do
num = newNum
I know if you declare the variable readOnly, you can't use the first method to change it, but what's the concept behind it? Is it just because the second method with the setter can be called outside of its own class? Like if the class's instance was called 'sample'.
[sample setNum: newNum]
but then if you are changing the variable inside the class, either way is fine?
In Objective-C, what's the difference between [self setVariable:
newStuff] and variable = newStuff?
To be absolutely pedantic, one of them assigns the variable property the value in newStuff, whereas the other one assigns the value of newStuff to the iVar variable, but what I think you had in mind was a comparison between [self setVariable:
newStuff] and self.variable = newStuff. In that case, nothing is different, the compiler will expand case 2 out to case 1.
I know if you declare the variable readOnly, you can't use the first
method to change it, but what's the concept behind it? Is it just
because the second method with the setter can be called outside of its
own class? Like if the class's instance was called 'sample'.
readonly variables are important in cases where certain properties are private to the implementation of a class, but should be visible to other classes.
For example, if I were writing a Stack, I might want to expose the count of the number of items on the stack, but it would be a very bad idea for other classes to be able to write to the count variable. If I weren't smart and were using something like a count variable, I would want to be able to adjust the count of the semaphore internally (meaning you need it to be internally readwrite), so I declare a visibly readonly property so other classes can get it, but declare it internally readwrite so I can modify it:
//.h
#interface CFExampleStack : NSObject
#property (nonatomic, assign, readonly) int count; //readonly
#end
//.m
#interface CFExampleStack ()
#property (nonatomic, assign) int count; //readwrite
#end
Is it just because the second method with the setter can be called outside of its own class?
Well, that depends on how your instance variable is declared. By default, instance variables are #protected, i. e. they can be accessed from within the class and its subclasses only. However, if you explicitly declare an ivar as #public, then you can access it outside the class, using the C struct pointer member operator ->:
obj->publicIvar = 42;
However, this is not recommended, since it violates encapsulation.
Furthermore, if you use a custom setter method, then you have the opportunity to do custom actions when a property of an instance is updated. For example, if one changes the backgroundColor property of a UIView, it needs to redraw itself in addition to assigning the new UIColor object to its appropriate ivar, and for that, a custom setter implementation with side effects is needed.
Additionally, there are retained ("strong") and copied properties in case of instance variables that hold object. While writing a setter for a primitive type such as an integer is as simple as
- (void)setFoo:(int)newFoo
{
_foo = newFoo;
}
then, in contrast, a retained or copied property needs proper memory nanagement calls:
- (void)setBar:(Bar *)newBar
{
if (_bar != newBar) {
[_bar release];
_bar = [newBar retain]; // or copy
}
}
Without such an implementation, no reference counting would take place, so the assigned object could either be prematurely deallocated or leaked.
One more important difference...
Whenever you use self.prop KVC comes into play and you can observe the changes in the object, while _prop bypasses it.

Objective-C synthesize property name overriding

I am trying to understand the purpose of the synthesize directive with property name overriding. Say that I have an interface defined as follow:
#interface Dummy ... {
UILabel *_dummyLabel;
}
#property (retain, nonatomic) UILabel *dummyLabel;
And in the implementation file, I have:
#synthesize dummyLabel = _dummyLabel;
From what i understand, "dummyLabel" is just an alias of the instance variable "_dummyLabel". Is there any difference between self._dummyLabel and self.dummyLabel?
Yes. self._dummyLabel is undefined, however _dummyLabel is not.
Dot syntax expands out to simple method invocations, so it's not specific to properties. If you have a method called -(id)someObject, for example in the case of object.someObject, it will be as if you wrote [object someObject];.
self.dummyLabel //works
self._dummyLabel //does not work
dummyLabel //does not work
_dummyLabel //works
[self dummyLabel]; //works
[self _dummyLabel]; //does not work
Your understanding is incorrect. dummyLabel is the name of the property, and is not an alias for the instance variable - the instance variable is only called _dummyLabel. So the following holds for an instance of Dummy called myObject:
[myObject dummyLabel] works
myObject.dummyLabel works
[myObject _dummyLabel] fails
myObject._dummyLabel fails
myObject->dummyLabel fails
myObject->_dummyLabel depends on the visibility of the ivar (#public, #private, #protected)
[myObject valueForKey: #"dummyLabel"] works
[myObject valueForKey: #"_dummyLabel"] depends on the implementation of +accessInstanceVariablesDirectly (i.e. it will work in the default case where +accessInstanceVariablesDirectly returns YES).
The advantage of having another name
for the ivar than for the property is
that you can easily see in the code
when you are accessing one or the
other - Andre K
I'm not able to find a 'comment' button so I'm having to post as an 'answer'.
Just wanted to expand on Andre's comment - by knowing when you are using the synthesized properties vs the vanilla variable, you know (especially in case of setters) when a variable is being retained/copied/released automatically thanks to your nice setter, vs being manipulated by hand.
Of course if you are doing things right, you probably don't need the help of a setter to retain/release objects properly! But there can be other scenarios too where referring to your ivars as self.ivar instead of _ivar can be helpful, such as when you are using custom setters/getters instead of the default synthesized ones. Perhaps every time you modify a property, you also want to store it to NSUserDefaults. So you might have some code like this:
#interface SOUserSettings : NSObject {
BOOL _autoLoginOn;
}
#property (nonatomic, assign) BOOL autoLoginOn;
#end
#implementation SOUserSettings
#synthesize autoLoginOn = _autoLoginOn;
- (void)setAutoLoginOn:(BOOL)newAutoLoginOnValue {
_autoLoginOn = newAutoLoginOnValue;
[[NSUserDefaults standardUserDefaults] setBool:_autoLoginOn forKey:#"UserPrefAutoLoginOn"];
}
#end
Note: This is just illustrative code, there could be a thousand things wrong with it!
So now, in your code, if you have a line that says _autoLoginOn = YES - you know it's not going to be saved to NSUserDefaults, whereas if you use self.autoLoginOn = YES you know exactly what's going to happen.
The difference between _autoLoginOn and self.autoLoginOn is more than just semantic.
I don't see any big advantage of
renaming _dummyLabel to dummyLabel
In some ObjC runtimes you have a hard time making instance variables invisible to users of the class. For them sticking some prefix (or suffix) on your instance variables can make it clear (or more clear) that you don't want anyone messing with your variables. However you don't want that gunk on your public functions. This lets you get it off.
It could also be useful if you need to maintain an old interface with one set of names at the same time as a new set of APIs with a new set of names (setLastname vs. setSurname).
Old post, but I think its important to mention, that it is recommended to access variables via getters and setters (so, with dot notation). Accessing a field directly (_ivar) is strongly recommended only when initializing it.
There is some good Apple's article:
https://developer.apple.com/library/ios/#documentation/cocoa/conceptual/ProgrammingWithObjectiveC/EncapsulatingData/EncapsulatingData.html
Last paragraph:
You should always access the instance variables directly from within
an initialization method because at the time a property is set, the
rest of the object may not yet be completely initialized. Even if you
don’t provide custom accessor methods or know of any side effects from
within your own class, a future subclass may very well override the
behavior.

NSMutableArray with only a particular type of objects

is it possible to specify that a NSMutableArray can only contain a certain type of objects.
For example, if I want to store only this kind of objects :
#interface MyObject : NSObject {
UInt8 value;
}
In order to be able to use the instance variable like this :
- (void)myMethod:(NSMutableArray *)myArray{
for (id myObject in myArray){
[self otherMethod:myObject.value];
}
}
because I'm getting this error :
request for member 'value' in something not a structure or union
Thank you for your help
It sounds like you're coming from a Java/C# type background where limits can be imposed on collections.
Collections in Cocoa don't follow that pattern. There is no way to set a restriction on what type of objects can be inserted (unless you write a wrapper class that enforces this).
Objective-C, by design, follows the "if it walks like a duck and it quacks like a duck, then it most probably is a duck" philosophy. That is to say that rather than checking whether an object is a particular type, you should be checking whether it can do what you want it to do regardless of its type.
You can do this using respondsToSelector:.
Finally, your problem isn't actually related to the fact that the array has no restrictions. Your object doesn't appear to declare the instance variable value as a property, or expose any accessor methods for it.
This is why you're seeing the error when you try myObject.value. That syntax in Objective-C is how you access properties.
The default scope for instance variables in Objective-C is #protected, which means anything outside your class can't access them without going through an accessor method of some kind.
You need to declare and define the methods - (UInt8)value and - (void)setValue:(UInt8)aValue and use them.
Alternatively, you could declare it as a property.
You are getting that error, because for as far as Objective-C is concerned, myObject is of the non-type id, which doesn't support the value property. To make Objective-C aware of the fact it's always dealing with a MyObject in this loop, you'll have to tell it the myObject object is an instance of MyObject.
for (MyObject *myObject in myArray) {
Also, you have to make sure the value ivar is accessible using dot-notation by implementing getter and setter methods for it. You can do this yourself by implementing -value and -setValue:, or you can use #property and #synthesize to let Objective-C do this.
Objective-C doesn't work like that. You need to use [myObject value] (which will work irrespective of the kind of object, as long as it responds to -[value]. If you only want one type of objects in it, insert only that type of objects.
You would have to write a wrapper-class for the NSMutableArray, see for example this question.
Subclass NSMutableArray and override methods that mediate the addition of objects to the array. You would check the object type in these overridden methods, only calling [super addObject:xyz] if the type is accepted.
maybe you can use protocol:
#protocol Person <NSObject>
#end
#interface Person : NSObject <Person>
#end
to use:
NSArray<Person>* personArray;