3

I have a class, let's say Chicken, and I want a class-level method to enumerate all the currently existing Chickens. To do this, I keep a class-level NSMutableArray and add self to this in the init method.

This is great and my enumeration method simply returns a (non-mutable) pointer to this array.

The problem is that I can no longer deallocate a chicken by removing all pointers to it, as there is always a strong pointer left in the array.

E.g. If I do this...

Chicken *chick = [[Chicken alloc] init];
// Do something with the chick
chick = nil;

The chicken lives on because there is a strong pointer to it in the array. I could have a -[Chicken kill] method, which removes it from the array, but that's not neat.

What's the neatest way around this?

jscs
  • 63,694
  • 13
  • 151
  • 195
Thickycat
  • 894
  • 6
  • 12
  • Use a weak reference in the array. NSValue has support for nonretained objects, but if you want an auto-zeroing reference, you could use the second half of this post: http://stackoverflow.com/questions/4224495/using-an-nsstring-in-a-switch-statement/13114988#13114988 – Richard J. Ross III Feb 24 '14 at 23:59
  • This is a hard design to maintain once you take into account multithreading. Be careful. – Léo Natan Feb 24 '14 at 23:59

2 Answers2

4

Use + (NSValue *)valueWithNonretainedObject:(id)anObject values inside your array. To simplify your API, you can create a wrapping method of +arrayOfAllChickens which would iterate all non-retained values and add them to an array and return that.

Remember to remove the values from the array in dealloc or you could hit a bad access crash.

Léo Natan
  • 56,823
  • 9
  • 150
  • 195
  • 2
    This is the correct approach, just note that this will not auto-zero the reference, meaning you could have issues with EXC_BAD_ACCESS should you try to send a message to a dead chicken. – Richard J. Ross III Feb 25 '14 at 00:00
  • @RichardJ.RossIII Just added a comment for that exactly. :-) "Dead chicken" is a nice touch! – Léo Natan Feb 25 '14 at 00:00
  • Thanks Leo, I wanted to mark both answers as correct but seems that's not possible so I went for the NSHashTable as I'm using that although your 'indirect with another class' method was the kind of thing that was floating around in my head when I was considering how to keep my chickens. Hmmn, I may call the NSHashTable* 'coup'... – Thickycat Feb 25 '14 at 00:42
  • @Thickycat Sure, now worries. It's the better method. – Léo Natan Feb 25 '14 at 00:43
4

You can use [NSHashTable weakObjectsHashTable] to store objects. It is basically an array holds weak references. It is safe, and clean.

If NSHashTable is not available, or suitable (you want to hold key-value pair like NSDictionary) but you have ARC with __weak supported, you can use a wrapper object to hold weak reference.

I use block to hold weak ref in this example

NSMutableDictionary *globalDict = [NSMutableDictionary dictionary];

// put object
id obj = [Foo new];
__weak id weakRef = obj;
globalDict[key] = [^() { return weakRef; } copy];

// read object
id (^block)(void) = globalDict[key];
id obj = block ? block() : nil;
if (!obj) {
    [globalDict removeObjectForKey:key];
}
Bryan Chen
  • 45,816
  • 18
  • 112
  • 143
  • Another good option. As usual, one should be careful with thread-safety and non-retained pointers. – Léo Natan Feb 25 '14 at 00:06
  • As I'm only interested in having a bag of chickens and don't worry about the order, the NSHashTable looks simplest if I'm understanding correctly that +weakObjectsHashTable will create a table such that killing a chicken will remove it from the table? – Thickycat Feb 25 '14 at 00:29
  • @Thickycat yes, it is what you want. – Bryan Chen Feb 25 '14 at 00:32