How to create an array of non-retained objects in arc? - iphone

How to create an array of non-retained objects in arc? These objects are observers in this array. Currently, I'm creating this array in this way:
_observers = CFBridgingRelease(CFArrayCreateMutable(NULL, 0, NULL));
The problem is the code crashes sometimes when making notifications in this line:
for (NSInteger i = [_observers count] - 1; i >= 0; i--) {
// crash line
id<ListModelObserver> observer = (id<ListModelObserver>)[_observers objectAtIndex:i];
...
I have zombies enabled on, and clearly see observer object classname in debug console. The observer object should be already removed from _observers during dealloc... The only thing that comes to my mind is _observers array somehow retains its objects. Any ideas?

You're releasing the array, not the objects. If you want to have a collection of unsafe unrestrained pointers to objects, then either use a C array or set up a CFArrayRef that doesn't include any call back functions.

Related

How to safely loop over a mutable array while modifying it?

I have a controller which has an array holding actors. An actor is a object which will be called by the controller.
The problem: The controller iterates over the actors array and sends each actor an -actionMessage. The actor can create and register another actor with the controller, or remove an actor or even itself from the controller's actors array. It is routed through two methods:
-registerActor:(Actor*)actor;
-unregisterActor:(Actor*)actor;
So while the controller iterates over the actors array, the list of actors can change.
Edit: And any newly added actor MUST go through the loop as well.
What is best practice to deal with this problem? Should I create a copy of the actors array before iterating over it?
Create a copy of your mutable array and iterate over that.
NSArray *loopArray = [NSArray arrayWithArray: yourActorArray];
Or
NSArray *loopArray = [yourActorArray copy];
//in this case remember to release in nonARC environment
This is what I usually do...
NSMutableArray *discardedItems = [NSMutableArray array];
SomeObjectClass *item;
for (item in originalArrayOfItems) {
if ([item shouldBeDiscarded])
[discardedItems addObject:item];
}
[originalArrayOfItems removeObjectsInArray:discardedItems];
hoping this helps.
The standard procedure for enumerating through a mutable array that needs to be altered as you step through it is to make a copy and iterate through that, altering the original. I'm guessing the NSMutableArray of actors is a property belonging to your controller, and that registerActor: and unregisterActor: both alter this array. If you step through a copy you can remove actors from the original property through the methods without altering the copy.
NSMutableArray *actorArrayCopy = [self.actorArray copy];
for (id object in actorArrayCopy){
//do stuff
}
In some cases, you can scrap fast enumeration and use a standard for loop, however, this is risky here. If objects are inserted or removed from the array, the indexes will shift (AFAIK), meaning you may end up skipping elements or going over an element multiple times.
Some people store elements to be altered (such as removed) in a separate array within the fast enumeration and perform all the changes at once once the enumeration is done, but you are using separate methods for adding and removing elements; the elements themselves determine what should happen and notify you. This would make things more complicated, so a copy will probably work best.
You can avoid making a copy of the array by doing this:
for(int i=0; i<[array count]; i++)
{
if(condition)
{
[array removeObjectAtIndex:i];
i --;
continue;
}
}
You should use a set instead of an array. Then you can copy the set, and after the operation is done, take a diff to see whats changed.
I've made a macro called smartFor that allows you to do the same set up as a for-loop fast enumeration but handles modification of array. It takes advantage of a few C and CLANG exploits and is very simple and clean.
Macro:
///Allows for modifying an array during virtual fast enumeration
#define smartFor(__objectDeclaration, __array, __block) {\
int __i = 0;\
while (__i < __array.count) {\
__objectDeclaration = __array[__i];\
NSObject *__object = __array[__i];\
[__block invoke];\
if ([__array indexOfObjectIdenticalTo:__object] != NSNotFound && ((int)[__array indexOfObjectIdenticalTo:__object]) <= __i) {\
__i = ((int)[__array indexOfObjectIdenticalTo:__object])+1;\
}\
}\
}
It automatically handles all of the [basic] wonky things you may do like adding objects (anywhere in the array, at any point during the "fast enumeration"[1]), removing objects, adjusting the index of objects, modifying objects, etc.
Example Usage:
smartFor(NSNumber *aNumber, myArray, ^{
if ([aNumber isEqualToNumber:#(3)]) {
[myArray removeObjectIdenticalTo:aNumber];
}
});
[1]Note: It's not technically fast enumeration on a strict definition/performance basis, obviously, but behaves just like it for all intents and purposes

removing items from NSmutableArray

I have two nsmutablearrays
array1: searchedStores = [[NSMutableArray alloc] init];
array2: allStores; ///it holds Stores information and it is also
NSMutableArray
Now if i add objects in "searchedStores" Array
for (int i= 0; i < [allstores count]; i++){
Franchise *store = [allStores objectAtIndex:i];
if ([store.ZipCode hasPrefix:str]) /// where str is searched string
{
[searchedStores addObject:store]; // some stores information will be inserted in "searchedStores" array
}
}
Now if i remove objects from searchedStores array it will also remove objects from "allStores" array
to remove objects i am writing
[searchedStores removeAllObjects];
How can i remove objects only from "searchedStores" array. and "allStores" Array should keep its objects.
IF you are just assigning allstores array with searchedstores array then changes made in searched stores array will be reflected to allstores. You need to allocate new memory to allstores and then add objects from searchedstores array to it upon your logical behavior.
Every array is totally independent. If removing objects from one array effects the other, you don't have two arrays, but only two pointers to the same array. You need to instantiate (alloc/init or mutableCopy) both. Feel free to show your initialization code as well.

OBJECTIVE-C Stack with array of int

I would like to create a stack with array of int (2 dimensional : int[9][9]), but I can't.
I tried the NSMutableArray which needs objects. So I tried to use NSValue to transform my array to a NSObject, but I keep having lot ouf trouble.
Can you please give me some help ?
I push an array (a grid) in the (void)saveGrid function.
I need to recover the last grid in the (IBAction)undo function.
Here is my last-try code source : (where Im transforming my 2D-array (9*9) to a 1D-array (81))
- (void)saveGrid {
int longGrid[81];
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
longGrid[(i+1)*(j+1)-1] = matrix[i][j];
}
}
NSValue *valObj;
valObj = [NSValue value:&longGrid withObjCType:#encode(int **)];
[TGrid addObject:valObj];
}
- (IBAction)undo:(id)sender {
[TGrid removeLastObject];
int *valPtr;
[[TGrid lastObject] getValue:&valPtr];
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
matrix[i][j] = valPtr[(i+1)*(j+1)-1];
}
}
}
(TGrid is NSMutableArray typed.)
Thanks for your help !
I don't really understand your thinking. You want to use an NSArray, correct? Or NSMutableArray? Either way, you can't store ints in these. You can only store objects, so an integer would be an NSNumber object. It's actually a subclass of NSValue, so you don't need to use NSValue.
So with that it's pretty easy to create an NSMutableArray, populate it with the numbers, which are of the NSNumber object, and then create another NSMutableArray and populate that one with a bunch of NSMutableArrays. There you have it, a 2 dimensional array.
Secondly, It's not a good idea to subclass NSArray, NSDictionary, NSString, and the mutable versions of that. In Cocoa, many classes are actually implemented as class clusters, which are a bunch of implementation-specific classes hidden behind a common interface. When you make an NSString object, you might actually end up getting an NSLiteralString, NSCFString, NSSimpleCString, NSBallOfString, or any number of undocumented implementation-detail objects.
You should use NSNumber. Checked that out? Otherwise you could go for an C Array.
If your problem is to put a 2D array of int into an object, why not do just that? Create a class containing one property of type int[9][9], create an object of that class and put your array in it.
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[CALayerArray addObject:]: unrecognized selector sent to instance 0x4b36ce0'
I think that TGrid is getting autoreleased. Try this:
TGrid = [[NSMutableArray alloc] initWithCapacity:10];
Then release it in your -dealloc method of course.

How to swap values in NSMutableArray?

This piece of codes segment fault on me, any idea why? allButtons is a NSMutableArray, it contains 3 objects, a=0, b=1, a and b are int type
if(a != -1 && b!= -1){
//Swap index in "allButtons"
id tempA = [allButtons objectAtIndex:a];
id tempB = [allButtons objectAtIndex:b];
[allButtons replaceObjectAtIndex:a withObject:tempB]; //Seg fault here?????
[allButtons replaceObjectAtIndex:b withObject:tempA];
needLoad = false;
[self setUpButtons];
}
EDIT:
NSMutableArray *allButtons = //fetch the array from Coredata. This work since I display the data onto the screen, plus, [allButtons count] return 3, and a=0, b=1
f(a != -1 && b!= -1){
//Swap index in "allButtons"
[allButtons exchangeObjectAtIndex:a withObjectAtIndex:b];
needLoad = false;
[self setUpButtons];
}
The first call to replaceObjectAtIndex: will release the old object (tempA), but that shouldn't cause a seg fault. As #Zoran mentioned try logging the retainCount for tempA and verify its count.
Also for swapping elements in an array, you should use exchangeObjectAtIndex:withObjectAtIndex instead of replaceObjectAtIndex:withObject. It's supported from iPhone 2.0.
Just because you have said
NSMutableArray *allbuttons = // something
doesn't mean that it is definitely an NSMutableArray, it just means that the compiler thinks that it will be a NSMutableArray.
If it's from CoreData, it's probably just an NSArray so the method calls you are trying won't work - you'll get unrecongnised selector or something like that.
You will have to convert it to a mutable array first
NSArray *coreData = // core data call
// Create a mutable copy
// NB This means that you are now working on a copy, not the original :)
NSMutableArray *allButtons = [coreData mutableCopy];
tempA is going to be released when you call the first replaceObjectAtIndex. Keep that in mind when calling this... I have no idea why releasing tempA would seg fault for you, examine what its dealloc does perhaps.
Check the retain count of tempA to verify that it is indeed dealloc-ed (not simply released) by the call to replaceObjectAtIndex like so:
id tempA = [allButtons objectAtIndex:a];
NSLog(#"retain count for tempA: %i", [tempA retainCount]);
If you see a retain count of 1 at this level, then your object tempA is being dealloc-ed by the call to replaceObjectAtIndex
Please read and understand the Cocoa rules on object ownership. Note that you have not claimed ownership over the objects referenced by tempA and tempB and you must therefore heed the following:
A received object is normally guaranteed to remain valid within the method it was received in ... (although you must also take care if you modify an object from which you received another object). That method may also safely return the object to its invoker.
Basically, the line:
[allButtons replaceObjectAtIndex:a withObject:tempB];
May cause tempA to be deallocated. This means that the subsequent line will cause allButtons to send a retain message to an invalid object, hence the seg fault. To fix the problem, you need to retain tempA before the swap and release it or autorelease it after.
NB it's wise to forget about retain counts. Unless you are fully aware of the implementation of all objects that touch your objects, you cannot make any assumptions about what the retain count of an object is. For instance, there's no rule that says that the implementation of NSMutableArray will only ever retain its elements once.
Use this method pass the appropritate index
exchangeObjectAtIndex:withObjectAtIndex:

Detect if one position in Array is already initiated

I need to check specific positions in an NSArray to see if they have already been initialized, but I am having trouble. I tried to do the following, but it causes my application to crash!
if ((NSMutableArray *)[arrAllBlocks objectAtIndex:iLine] == nil)
{
[arrAllBlocks insertObject:[[NSMutableArray alloc] init] atIndex:iLine];
}
NSMutableArray *columArray = (NSMutableArray *)[arrAllBlocks
objectAtIndex:iLine];
[columArray insertObject:newBlock atIndex:iColumn];
What is the best to do this? I already tried some methods like isValid, and things like that!
You have a few options here:
Option 1: Pre-fill the array with instances of NSNull, and then use the code given by Dave DeLong in his answer.
Option 2: (Similar to #1) pre-fill the array with instances of NSMutableArray, and then have no extra code at all. (If you're going to pre-fill, you may as well do this).
Option 3: Do not pre-fill the array, but insert items dynamically as required. This will be almost identical to a pre-fill if the first iLine is near the maximum:
while([arrAllBlocks count] <= iLine)
{
[arrAllBlocks addObject:[NSMutableArray arrayWithCapacity:0]];
}
NSMutableArray *columArray = (NSMutableArray *)[arrAllBlocks
objectAtIndex:iLine];
[columArray insertObject:newBlock atIndex:iColumn];
Option 4: Use a dictionary to maintain the list of NSMutableArrays:
NSString *key = [NSString stringWithFormat:#"%d", iLine];
NSMutableArray *columnArray = [dictAllBlocks objectForKey:key];
if (columnArray == nil)
{
columnArray = [NSMutableArray arrayWithCapacity:0];
[dictAllBlocks setObject:columnArray forKey:key];
}
[columArray insertObject:newBlock atIndex:iColumn];
How to choose:
If the maximum value for iLine is not enormous, I would go with option #2. A handful of NSMutableArrays initialized to zero capacity will take up very little memory.
If the maximum value for iLine is enormous, but you expect it to be accessed sparsely (i.e., only a few values of iLine will ever be accessed), then you should go with Option #4. This will save you from having to fill an NSMutableArray with objects that never get used. The overhead of converting the string-value key for the dictionary will be less than the overhead for creating all of those blanks.
If you're not sure, try out each option and profile them: measure your memory usage and the time required to execute. If neither of these options work, you may have to explore more complex solutions, but only do that if it turns out to be necessary.
A note of caution:
The original code that you posted has a memory leak in the following line:
[arrAllBlocks insertObject:[[NSMutableArray alloc] init] atIndex:iLine];
The NSMutableArray objects that you initialize here are never released. When you call [[NSMutableArray init] alloc], a brand new object is created (with a reference count of one). The insertObject method then adds that new object to arrAllBlocks, and retains it (increasing its retain count to 2). Later, when you release arrAllBlocks, the new array will be sent a release message, but that will only reduce its retain count to one again. At that point, it will stick around in RAM until your program exits.
The best thing to do here is to use [NSMutableArray arrayWithCapacity:0] instead (as I have done in my examples). This returns a new NSMutableArray, just the same as your code did, but this instance has already been autoreleased. That way, arrAllBlocks can take ownership of the new object and you can be sure that it will be released when appropriate.
You can't. NSArray (and its subclass NSMutableArray) do not allow you to insert nil into the array. That's clearly outlined in the documentation.
If, for some reason, you need to have "empty" values in an array, then you should insert [NSNull null] instead and test for that. From the docs: "The NSNull class defines a singleton object used to represent null values in collection objects (which don’t allow nil values)."
UPDATE:
This means you could change your code very simply to this:
if ([[arrAllBlocks objectAtIndex:iLine] isEqual:[NSNull null]]) {
[(NSMutableArray *)arrAllBlocks insertObject:[NSMutableArray array] atIndex:iLine];
}
NSMutableArray *columnArray = (NSMutableArray *)[arrAllBlocks objectAtIndex:iLine];
[columnArray insertObject:newBlock atIndex:iColumn];
To check for NSNull you can simply compare against the pointer, since it's a Singleton:
if ([NSNull null] == [arrAllBlocks objectAtIndex:iLine]) {
[arrAllBlocks insertObject:[NSMutableArray array] atIndex:iLine];
}
NSMutableArray *columnArray = [arrAllBlocks objectAtIndex:iLine];
[columnArray insertObject:newBlock atIndex:iColumn];
I also removed the unsightly casts. Casting is rarely necessary in Objective-C. It usually just adds noise, and can hide real bugs. Since you're experiencing crashes, it's worth removing the casts from this code and listen to what the compiler has to tell you about it.
Telling the compiler to ignore warnings for a piece of code does not make the underlying problem with it go away!