Accessing collection through KVC (to protect collection and be KVO compliant) - objective-c

I have a class Test which has an array of Foos. I want to provide access to the Foos without exposing the ivar directly. I'm trying to make this KVC compliant (also to pave the way for KVO compliance). I have:
Test.h
#interface Test : NSObject
{
NSMutableArray *foos;
}
#property (readonly, copy) NSMutableArray *foos;
#end
Test.m
- (id) init
{
self = [super init];
if (self != nil)
{
foos = [[NSMutableArray array] retain];
}
return self;
}
- (NSMutableArray*) foos
{
return [self mutableArrayValueForKey:#"foos"];
}
- (NSUInteger)countOfFoos
{
return [foos count];
}
- (id)objectInFoosAtIndex:(NSUInteger)index
{
return [foos objectAtIndex:index];
}
- (NSArray *)foosAtIndexes:(NSIndexSet *)indexes
{
return [foos objectsAtIndexes:indexes];
}
- (void)insertObject:(id)key inFoosAtIndex:(NSUInteger)index
{
[foos insertObject:key atIndex:index];
}
- (void)insertFoos:(NSArray *)foosArray atIndexes:(NSIndexSet *)indexes
{
[foos insertObjects:foosArray atIndexes:indexes];
}
- (void)removeObjectFromFoosAtIndex:(NSUInteger)index
{
[foos removeObjectAtIndex:index];
}
- (void)removeFoosAtIndexes:(NSIndexSet *)indexes
{
[foos removeObjectsAtIndexes:indexes];
}
This enters an infinite loop when a client tries to add a Foo:
Test *test = [[Test alloc] init];
NSMutableArray *foos = test.foos;
[foos addObject:#"adding object"]; // infinite loop here
What am I doing wrong?

- (NSMutableArray*) foos
{
return [self mutableArrayValueForKey:#"foos"];
}
An accessor should not use KVC to get the value of the property being accessed; the idea is that KVC goes through the accessors, because the accessors are closer to the value than KVC is.
The correct implementation of foos should return a copy, mutable or otherwise, of the array. Here's how I'd do it:
- (NSArray *) foos
{
return [[foos copy] autorelease];
}
I would also make all of the accessors public. Anything that wants to mutate the array or randomly access elements at specific indexes can do so that way. It's still safe and encapsulated because they're going through your accessors, not directly accessing the array.
There's not really any reason to use the KVC protocol methods yourself unless you don't know what key you'll access at the time you write the code. For example, if you were writing the nib loader or the Cocoa Bindings system, you would use KVC.

The problem is that the proxy NSMutableArray returned by mutableArrayValueForKey: first has to get the real array, which it does through the "foos" method. Since that's the one that returns a proxy NSMutableArray it enters an infinite loop. One solution is to use another name:
- (NSMutableArray*) mutableFoos
{
return [self mutableArrayValueForKey:#"foos"];
}

I spent a very long time on this problem and wanted to get this through an accessor. I wanted to clarify in the answer for those coming in. This is what I did:
#property (nonatomic,readonly,getter=getTheFoos) NSMutableArray* foos;
Then obviously implemented:
- (NSMutableArray*)getTheFoos {
return [self mutableArrayValueForKey:#"foos"];
}
Had to be careful though, getFoos appears to be an (undocumented) KVC accessor, because this sends it into the same loop.
Then onto KVO:
Test* test= [[Test alloc] init];
NSObject* obj= [[NSObject alloc] init];
NSMutableArray* arrTheData= test.foos;
[test.foos insertObject:obj atIndex:0];
[arrFoos insertObject:obj atIndex:0];
arrFoos can read the updated, mutated array (it will have two objects in it), but inserting into it will not fire KVO. Somewhere on my adventures, I saw that the return from mutableArrayValueForKey: doesn't return an NSMutableArray*, but a subclass of it, which might be the cause of it.

Related

In objective C lazy instantiation, why don't we touch the setter?

In objective C, its common practice to instantiate internal class arrays (and the like) in a lazy manner.
So if you call on the getter, it first checks if the array isn't nil, and allocates memory for it if needed.
But what about the setter?
If you are trying to insert some value into one of the array cells, since we did not allocate memory for it yet - where does it go?
I'm missing something here, clearly. Would be happy for a clarification.
I'm not sure I understand your question, but if you do this:
#property (nonatomic, strong) NSMutableArray* myArray;
...
- (NSMutableArray *) myArray {
if(!_myArray) {
NSLog(#"created");
_myArray = [[NSMutableArray alloc] init];
}
return _myArray;
}
...
[self.myArray addObject:#"test"];
The getter is actually getting called when you call addObject:, so you'll see "created" being logged.
So #property declarations are syntactic sugar for declaring, in the case of objects, pointers to instance variables. The "nonatomic" refers to the type of getter and setter automatically created (in this case "non thread safe.") And the "strong" is an indicator to ARC to increase the retain count of the variable.
So when you declare:
#property (nonatomic, strong) NSMutableArray* myArray;
This is what really gets created in your class - just a pointer to your hidden instance variable.
#implementation MyClass {
NSMutableArray *_myArray;
}
As you can see in the getter, you are initializing the _myArray pointer to point to a new NSMutableArray:
- (NSMutableArray *) myArray {
if(!_myArray) {
NSLog(#"created");
_myArray = [[NSMutableArray alloc] init];
}
return _myArray;
}
However in the setter, you are just updating the pointer to a variable you have already created.
self.myArray = [[NSMutableArray alloc] init];
This sends your class the following message:
- (void) myArray: (NSMutableArray *) myArray {
_myArray = myArray;
}
As you can, see the setter doesn't need any special initialization most of the time. The only time you want to create a custom setter is when you want to validate the incoming object has special properties. A contrived example is checking that the NSMutableArray is no larger than 10 objects:
- (void) myArray: (NSMutableArray *) myArray {
if (myArray.count < 10) {
_myArray = myArray;
}
}
Finally, I would like to point out that you can actually lazy instantiate objects using the short ternary operator and parenthetical return values. For example, the following statement:
- (NSMutableArray *) myArray {
return (_myArray = _myArray ?: #{}.mutableCopy);
}
Is equal to:
- (NSMutableArray *) myArray {
if(!_myArray) {
_myArray = [[NSMutableArray alloc] init];
}
return _myArray;
}
You can even macro this pattern into (WSM is my class prefix):
#define WSM_LAZY(object, assignment) (object = object ?: assignment)
So you can write statements like this:
- (NSMutableArray *) myArray {
return WSM_LAZY(_myArray, #{}.mutableCopy);
}
Or even use compound statement syntax to rewrite the original setter you presented as an example:
- (NSMutableArray *) myArray {
return WSM_LAZY(_myArray, ({
NSLog(#"created");
#{}.mutableCopy;
}));
}

Why my own Object not populate NSMutableArray property?

Hi i want to implement my own Objects to manage my data, i was trying to make a two classes.
Class Continents that contains a Continent Objects
Here is my implementation:
#implementation OsContinents
#synthesize continentes;
-(id)init{
return [super init];
}
-(NSUInteger)count{
NSLog(#"%u",[continentes count]);
return [continentes count];
}
-(void)add:(OsContinent *)continente{
[continentes addObject:continente];
}
-(OsContinent *)getElementByIndex:(NSUInteger)index{
return [continentes objectAtIndex:index];
}
-(void)deleteContinentByIndex:(NSUInteger)index{
return [continentes removeObjectAtIndex:index];
}
-(void)deleteContinent:(OsContinent *)objContinent{
return [continentes removeObject:objContinent];
}
-(NSMutableArray *)getAll{
return continentes;
}
#end
Next i want to populate *continents Property with "Continent" Objects like this.
OsContinents *continentesCollection = [[OsContinents alloc] init];
for (NSString *strContinente in [data allKeys]) {
OsContinent *con = [[OsContinent alloc] init];
[con setContinente:strContinente];
NSLog(#"%#",[con getContinente]);
[continentesCollection add:con];
}
NSLog(#"%u",[continentesCollection count]);
But allways got ZERO in de count Method.
Note: NSLog(#"%#",[con getContinente]) print de data OK, the Continent Object is OK, the problem is in the "*continentes" inside the Continents Object-
Any Clue?
Your initializer does nothing but initialize the superclass. Use it to set up your own class:
- (id)init
{
self = [super init];
if (self)
{
_continentes = [[NSMutableArray alloc] init];
}
return self;
}
Otherwise, continentes will remain nil. Messaging nil is valid: methods simply don't do anything, and return 0.
If you want to completely hide the underlying mutable array (which is perfectly fine) then don't advertise it in your .h file as a property. Instead, at the beginning of your #implementation, declare a semi-private instance variable:
#implementation OsContinents
{
NSMutableArray *_continentes;
}
I say "semi-private" because you can always use the runtime engine to introspect objects. But it'll be hidden from normal use. If you ever subclass your object, you can always move the instance variable declaration from your #implementation to your #interface so that subclasses can get at it.

NSMutableArray - force the array to hold specific object type only

Is there a way to force NSMutableArray to hold one specific object type only?
I have classes definitions as follow:
#interface Wheel:NSObject
{
int size;
float diameter;
}
#end
#interface Car:NSObject
{
NSString *model;
NSString *make;
NSMutableArray *wheels;
}
#end
How can I force wheels array to hold Wheel objects only with code? (and absolutely not other objects)
Update in 2015
This answer was first written in early 2011 and began:
What we really want is parametric polymorphism so you could declare, say, NSMutableArray<NSString>; but alas such is not available.
In 2015 Apple apparently changed this with the introduction of "lightweight generics" into Objective-C and now you can declare:
NSMutableArray<NSString *> *onlyStrings = [NSMutableArray new];
But all is not quite what it seems, notice the "lightweight"... Then notice that the initialisation part of the above declaration does not contain any generic notation. While Apple have introduced parametric collections, and adding a non-string directly to the above array, onlyStrings, as in say:
[onlyStrings addObject:#666]; // <- Warning: Incompatible pointer types...
will illicit the warning as indicated, the type security is barely skin deep. Consider the method:
- (void) push:(id)obj onto:(NSMutableArray *)array
{
[array addObject:obj];
}
and the code fragment in another method of the same class:
NSMutableArray<NSString *> *oops = [NSMutableArray new];
[self push:#"asda" onto:oops]; // add a string, fine
[self push:#42 onto:oops]; // add a number, no warnings...
What Apple have implemented is essentially a hinting system to assist with automatic inter-operation with Swift, which does have a flavour of type-safe generics. However on the Objective-C side, while the compiler provides some extra hints the system is "lightweight" and type-integrity is still ultimately down to the programmer - as is the Objective-C way.
So which should you use? The new lightweight/pseudo generics, or devise your own patterns for your code? There really is no right answer, figure out what makes sense in your scenario and use it.
For example: If you are targeting interoperation with Swift you should use the lightweight generics! However if the type integrity of a collection is important in your scenario then you could combine the lightweight generics with your own code on the Objective-C side which enforces the type integrity that Swift will on its side.
The Remainder of the 2011 Answer
As another option here is a quick general subclass of NSMutableArray which you init with the kind of object you want in your monomorphic array. This option does not give you static type-checking (in as much as you ever get it in Obj-C), you get runtime exceptions on inserting the wrong type, just as you get runtime exceptions for index out of bounds etc.
This is not thoroughly tested and assumes the documentation on overriding NSMutableArray is correct...
#interface MonomorphicArray : NSMutableArray
{
Class elementClass;
NSMutableArray *realArray;
}
- (id) initWithClass:(Class)element andCapacity:(NSUInteger)numItems;
- (id) initWithClass:(Class)element;
#end
And the implementation:
#implementation MonomorphicArray
- (id) initWithClass:(Class)element andCapacity:(NSUInteger)numItems
{
elementClass = element;
realArray = [NSMutableArray arrayWithCapacity:numItems];
return self;
}
- (id) initWithClass:(Class)element
{
elementClass = element;
realArray = [NSMutableArray new];
return self;
}
// override primitive NSMutableArray methods and enforce monomorphism
- (void) insertObject:(id)anObject atIndex:(NSUInteger)index
{
if ([anObject isKindOfClass:elementClass]) // allows subclasses, use isMemeberOfClass for exact match
{
[realArray insertObject:anObject atIndex:index];
}
else
{
NSException* myException = [NSException
exceptionWithName:#"InvalidAddObject"
reason:#"Added object has wrong type"
userInfo:nil];
#throw myException;
}
}
- (void) removeObjectAtIndex:(NSUInteger)index
{
[realArray removeObjectAtIndex:index];
}
// override primitive NSArray methods
- (NSUInteger) count
{
return [realArray count];
}
- (id) objectAtIndex:(NSUInteger)index
{
return [realArray objectAtIndex:index];
}
// block all the other init's (some could be supported)
static id NotSupported()
{
NSException* myException = [NSException
exceptionWithName:#"InvalidInitializer"
reason:#"Only initWithClass: and initWithClass:andCapacity: supported"
userInfo:nil];
#throw myException;
}
- (id)initWithArray:(NSArray *)anArray { return NotSupported(); }
- (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag { return NotSupported(); }
- (id)initWithContentsOfFile:(NSString *)aPath { return NotSupported(); }
- (id)initWithContentsOfURL:(NSURL *)aURL { return NotSupported(); }
- (id)initWithObjects:(id)firstObj, ... { return NotSupported(); }
- (id)initWithObjects:(const id *)objects count:(NSUInteger)count { return NotSupported(); }
#end
Use as:
MonomorphicArray *monoString = [[MonomorphicArray alloc] initWithClass:[NSString class] andCapacity:3];
[monoString addObject:#"A string"];
[monoString addObject:[NSNumber numberWithInt:42]]; // will throw
[monoString addObject:#"Another string"];
Since Xcode 7, generics are available in Objective-C.
You can declare a NSMutableArray as:
NSMutableArray <Wheel*> *wheels = [[NSMutableArray alloc] initWithArray:#[[Wheel new],[Wheel new]];
The compiler will give you a warning if you try to put non-Wheel objects in the array.
I could be wrong (I'm a noob), but I think, if you create a custom protocol and make sure the objects you are adding to the array follow the same protocol, then when you declare the array you use
NSArray<Protocol Name>
That should prevent objects being added that do not follow the said protocol.
as per i know.. before you added any object in wheels mutableArray, u have to add some check mark. Is the object which i am adding is class "wheel". if it is then add, other wise not.
Example:
if([id isClassOf:"Wheel"] == YES)
{
[array addObject:id)
}
Something like this. i dont remember the exact syntax.
I hope this will help (and work... :P )
Wheel.h file:
#protocol Wheel
#end
#interface Wheel : NSObject
#property ...
#end
Car.h file:
#import "Wheel.h"
#interface Car:NSObject
{
NSString *model;
NSString *make;
NSMutableArray<Wheel, Optional> *wheels;
}
#end
Car.m file:
#import "Car.h"
#implementation Car
-(id)init{
if (self=[super init]){
self.wheels = (NSMutableArray<Wheel,Optional>*)[NSMutableArray alloc]init];
}
return self;
}
#end
Xcode 7 allows you to define Arrays, Dictionaries, and even your own Classes as having generics. The array syntax is as follows:
NSArray<NSString*>* array = #[#"hello world"];
I don't believe there's any way to do it with NSMutableArray out of the box. You could probably enforce this by subclassing and overriding all the constructors and insertion methods, but it's probably not worth it. What are you hoping to achieve with this?
That's not possible; an NSArray (whether mutable or not) will hold any object type. What you can do is to create your own custom subclasses as already suggested by Jim. Alternatively, if you wanted to filter an array to remove objects that weren't of the type you want, then you could do:
- (void)removeObjectsFromArray:(NSMutableArray *)array otherThanOfType:(Class)type
{
int c = 0;
while(c < [array length])
{
NSObject *object = [array objectAtIndex:c];
if([object isKindOfClass:type])
c++;
else
[array removeObjectAtIndex:c];
}
}
...
[self removeObjectsFromArray:array otherThanOfType:[Car class]];
Or make other judgments based on the result of isKindOfClass:, e.g. to divide an array containing a mixture of Cars and Wheels into two arrays, each containing only one kind of object.
You can use the nsexception if you dont have the specific object.
for (int i = 0; i<items.count;i++) {
if([[items objectAtIndex:i] isKindOfClass:[Wheel class]])
{
// do something..!
}else{
[NSException raise:#"Invalid value" format:#"Format of %# is invalid", items];
// do whatever to handle or raise your exception.
}
}
Here's something I've done to avoid subclassing NSMutableArray: use a category. This way you can have the argument and return types you want. Note the naming convention: replace the word "object" in each of the methods you will use with the name of the element class. "objectAtIndex" becomes "wheelAtIndex" and so on. This way there's no name conflict. Very tidy.
typedef NSMutableArray WheelList;
#interface NSMutableArray (WheelList)
- (wheel *) wheelAtIndex: (NSUInteger) index;
- (void) addWheel: (wheel *) w;
#end
#implementation NSMutableArray (WheelList)
- (wheel *) wheelAtIndex: (NSUInteger) index
{
return (wheel *) [self objectAtIndex: index];
}
- (void) addWheel: (wheel *) w
{
[self addObject: w];
}
#end
#interface Car : NSObject
#property WheelList *wheels;
#end;
#implementation Car
#synthesize wheels;
- (id) init
{
if (self = [super init]) {
wheels = [[WheelList alloc] initWithCapacity: 4];
}
return self;
}
#end
protocol maybe a good idea:
#protocol Person <NSObject>
#end
#interface Person : NSObject <Person>
#end
to use:
NSArray<Person>* personArray;
There is one-header file project which allows this:
Objective-C-Generics
Usage:
Copy ObjectiveCGenerics.h to your project.
When defining a new class use the GENERICSABLE macro.
#import "ObjectiveCGenerics.h"
GENERICSABLE(MyClass)
#interface MyClass : NSObject<MyClass>
#property (nonatomic, strong) NSString* name;
#end
Now you can use generics with arrays and sets just as you normally do in Java, C#, etc.
Code:

Elegant and 'correct' multiton implementation in Objective C?

Would you call this implementation of a multiton in objective-c 'elegant'? I have programmatically 'disallowed' use of alloc and allocWithZone: because the decision to allocate or not allocate memory needs to be done based on a key.
I know for sure that I need to work with only two instances, so I'm using 'switch-case' instead of a map.
#import "Multiton.h"
static Multiton *firstInstance = nil;
static Multiton *secondInstance = nil;
#implementation Multiton
+ (Multiton *) sharedInstanceForDirection:(enum KeyName)direction {
return [[self allocWithKey:direction] init];
}
+ (id) allocWithKey:(enum KeyName)key {
return [self allocWithZone:nil andKey:key];
}
+ (id) allocWithZone:(NSZone *)zone andKey:(enum KeyName)key {
Multiton **sharedInstance;
#synchronized(self) {
switch (key) {
case KEY_1:
sharedInstance = &firstInstance;
break;
case KEY_2:
sharedInstance = &secondInstance;
break;
default:
[NSException raise:NSInvalidArgumentException format:#"Invalid key"];
break;
}
if (*sharedInstance == nil)
*sharedInstance = [super allocWithZone:zone];
}
return *sharedInstance;
}
+ (id) allocWithZone:(NSZone *)zone {
//Do not allow use of alloc and allocWithZone
[NSException raise:NSObjectInaccessibleException format:#"Use allocWithZone:andKey: or allocWithKey:"];
return nil;
}
- (id) copyWithZone:(NSZone *)zone {
return self;
}
- (id) retain {
return self;
}
- (unsigned) retainCount {
return NSUIntegerMax;
}
- (void) release {
return;
}
- (id) autorelease {
return self;
}
- (id) init {
[super init];
return self;
}
#end
PS: I've not tried out if this works as yet, but its compiling cleanly :)
I find singletons a bad idea and this looks about four times as horrible. The code is quite complex, you can be sure of spending a nice few hours chasing subtle bugs in it and you will probably never feel comfortable about it. That’s no good. You should throw this abomination away and wire your objects together in some other way that doesn’t require so much thinking.
If you like patterns, you can use something akin to Factory pattern to wire your objects. The Factory will take care of creating those two instances and passing them wherever needed. And the Factory will be a lot more simple than Multiton:
#interface Factory : NSObject {
Foo *foo1, *foo2;
}
#end
#implementation Factory
- (id) init {
[super init];
foo1 = [[Foo alloc] init];
foo2 = [[Foo alloc] init];
return self;
}
Of course you don’t have to create both instances at once. You can do anything you like there – cache, lazy load, anything. The point is leaving the Foo lifetime management up to the Factory, separate from the Foo code. Then it gets much easier. ¶ All the other objects that need Foo will be created and wired through Factory and will receive their Foo through a setter:
#implementation Factory
- (id) wireSomeClass {
id instance = [[SomeClass alloc] init];
[instance setFoo:foo1];
[instance setAnotherDependency:bar];
return [instance autorelease];
}
This is all much more straightforward then the code from your question.
Don't override alloc. The problem with overriding alloc to return a previously allocated instance of the class, as you do, is that when +sharedInstance calls [[Multiton alloc] init]... +alloc will return the old instance, then -init will re-initialize it! The best practice is to override -init, doing the cache lookup and calling [self release] before you return the cached instance.
If you're really concerned about the cost of that extra +alloc (it's not much), you also do your cache lookup in +sharedInstance and then ensure that all of your clients access the instance through +sharedInstance to avoid the extra alloc.
Point of order: How do you know that you'll only ever have two instances, or need to have two instances? (Or want to have two instances?) What, exactly, is the point of having a "Multiton"? (And is that even a word?)

Question on retain attribute with #property and #synthesize

I'm still pretty new to Objective-C coding (as evidenced by this question) and I think I'm not completely understanding how using the retain attribute in a #property declaration works.
Here is an example class:
#interface Foo : NSObject {
NSMutableArray *myArray;
}
#property (retain) NSMutableArray *myArray;
My understanding was that adding the retain attribute to the #property declaration (and using the necessary #synthesize delcaration in the implementation file) will basically do the following setter and getter for me:
- (void)setMyArray:(NSMutableArray *)newArray {
myArray = [[NSMutableArray alloc] initWithArray:newArray];
[newArray release];
}
- (NSMutableArray *)myArray {
return myArray;
}
Is this accurate or am I mistaken on how the retain attribute works?
Adding the retain attribute will actually generate this code:
- (void)setMyArray:(NSMutableArray *)newArray {
[newArray retain];
[myArray release];
myArray = newArray;
}
- (NSMutableArray *)myArray {
return myArray;
}
The reason the retain method is called on newArray before release on the old value is that if newArray and myArray are the same object, the array will be released before it is retained again.
It's really hard to do it right. Take a look at the article Memory and thread-safe custom property methods on Cocoa with Love by Matt Gallagher.
Here's one implementation that works, heavily inspired by that excellent article.
- (void)setSomeString:(NSString *)aString {
#synchronized(self)
{
if (someString != aString) // not necessary, but might improve
// performance quite a bit
{
[aString retain];
[someString release];
someString = aString;
}
}
}
- (NSString *)someString {
#synchronized(self)
{
id result = [someString retain];
}
return [result autorelease];
}
retain will not do a copy of the new value. It will retain the new value and release the old one.