NSMutableArray removeAllObjects issue - objective-c

I'm facing a problem with NSMutableArray that give me a Exc_Bad_Access without a reason.
I have a UITableView that contain around 700 records, i wanna to activate a filter process to it, i'm using the following method to filter the content of the UITableView:
- (void) filterTableContentWith:(int)_minValue andWith:(int)_maxValue {
[tableContent removeAllObjects];
tableContent = [[NSMutableArray alloc] initWithArray:originalTableContent copyItems:YES];
if (_minValue == 0 && _maxValue == 0) {
NSLog(#"This is mean that no filter is activate here");
} else {
for (int x = ([tableContent count] - 1); x >= 0; x--) {
if ([[[tableContent objectAtIndex:x] objectForKey:#"price"] intValue] < _minValue && [[[tableContent objectAtIndex:x] objectForKey:#"price"] intValue] > _maxValue) {
[tableContent removeObjectAtIndex:x];
}
}
}
NSLog(#"tableContent count = %#",[tableContent count]);
[self.tableView reloadData];
}
When i'm calling this method, it gives me Exc_Bad_Access on NSLog(#"tableContent count ...
I think that [tableContent removeAllObjects]; is releasing the array, but it's unreasonable.
Any help will be appreciated.

count returns an int, so change your NSLog to:
NSLog(#"tableContent count = %d",[tableContent count]);
and you'll be fine.

In addition to change your NSLog for count with %d, I think the following codes are better than removing contents within your loop:
...
} else {
NSPredicate* predicate = [NSPredicate predicateWithFormat:
#"price < %d AND price > %d", _minValue, _maxValue];
NSArray* array = [tableContent filteredArrayUsingPredicate:predicate];
tableContent = array;
}
...

Related

How to compare two dynamic array in without using loop objective c [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I am new in iOS and I am facing a problem regarding to compare two array. I tried this code
if ([arr1 isEqualToArray:arr2])
{
NSLog(#"Print the output to update...");
}
But this not work for me.Because my array is like this
arr1=[#"1",#"2",#"3",#"4",#"5",#"6"];
arr2=[#"2"];
So, I tried a code like this
NSSet *set1 = [NSSet setWithArray:arr1];
NSSet *set2 = [NSSet setWithArray:arr2];
if ([set1 isEqualToSet:set2]) {
// equal
}
But, this not work for me.In my case arr1 is from web service and arr2 is from core data.can you suggest any other suggestion to compare this two array.
In if condition I am updating my code and in else condition in am inserting
if([arr1 isEqualToArray:arr2])
{
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"EntityName" inManagedObjectContext:context]];
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
NSManagedObject* favoritsGrabbed = [results objectAtIndex:0];
[favoritsGrabbed setValue:#"1" forKey:#"Key"];
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
}
else
{
if (self.device) {
// Update existing device
[device setValue:Audit forKey:#"key1"];
[device setValue:MethodID forKey:#"key2"];
[device setValue:CheckPointID forKey:#"key3"];
[device setValue:GlobalStringChk forKey:#"key4"];
[device setValue:RegionID forKey:#"key5"];
[device setValue:BranchID forKey:#"key6"];
[device setValue:SiteID forKey:#"key7"];
[device setValue:AuID forKey:#"key8"];
[device setValue:userid forKey:#"key9"];
[device setValue:StringIndex forKey:#"key10"];
} else {
// Create a new device
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:#"EntityName" inManagedObjectContext:context];
[newDevice setValue:Audit forKey:#"key1"];
[newDevice setValue:MethodID forKey:#"key2"];
[newDevice setValue:CheckPointID forKey:#"key3"];
[newDevice setValue:GlobalStringChk forKey:#"key4"];
[newDevice setValue:RegionID forKey:#"key5"];
[newDevice setValue:BranchID forKey:#"key6"];
[newDevice setValue:SiteID forKey:#"key7"];
[newDevice setValue:AuID forKey:#"key8"];
[newDevice setValue:userid forKey:#"key9"];
[newDevice setValue:StringIndex forKey:#"key10"];
}
}
Hear,I need to compare array so that I can update the value in core data and if array are not equal then I need to insert them.So, I can not use loop.Please see its else condition if I used loop it insert data until loop runs and I want to insert one value at click.So, I can not use loop.
if you are trying to figure if elements from one array is present in another, you can try the following
NSArray * firstArray = #[#"1",#"2",#"3",#"4"];
NSArray * secondArray = #[#"2",#"10",#"20"];
for(int index = 0 ; index < [firstArray count] ; index++)
{
NSString * element = [firstArray objectAtIndex:index];
if([secondArray containsObject:element])
{
//do stuff
//This block will execute only when element == #"2"
}
}
for (int i = 0; i < array2.count; i++)
{
if ([array1 containsObject:[array2 objectAtIndex:i]])
{
NSLog(#"Array1 contains array2 object");
}
else
{
NSLog(#"Array1 do not contains array2 object");
}
}
You can try this. This is working for me with your above provided data.
Here are many ways.
Use nested loops to know the exact position of equal elements:
for(int i= 0; i<arr2.count; i++){
for(int j= 0; j<arr1.count; j++){
if(arr1[j] == arr2[i])
NSLog(#"index position: %d element %#:", j, array1[j]);
}
}
Or you can use containsObject: methods:
containsObject: Returns a Boolean value that indicates whether a given object is present in the array.
for(int i= 0; i<arr2.count; i++){
if ([arr1 containsObject:[arr2 objectAtIndex:i]]) {
// indicates whether a given object is present in the array.
}
}
From your comments and title I guess:
By "compare" and "is equal" you are meaning to test membership; and
Your second array only contains one item (so there is no need to loop)
If this is correct then change your line:
if([arr1 isEqualToArray:arr2])
to
if([arr1 containsObject:arr2[0]])
which checks whether arr1 contains the first element of arr2 (i.e. arr2[0]).
HTH
Try this
arr1=[#"1",#"2",#"3",#"4",#"5",#"6"];
arr2=[#"2"];
//compare result
BOOL bol = (arr1.count == arr2.count);
// if count equal
if (bol) {
for (NSInteger i = 0; i < arr1.count; i++) {
NSString *str1 = [arr1 objectAtIndex:i];
NSString *str2 = [arr2 objectAtIndex:i];
if (![str1 isEqualToString:str2]) {
bol = NO;
break;
}
}
}
or use block
NSArray *arr1=#[#"1",#"2",#"3",#"4",#"5",#"6"];
NSArray *arr2=#[#"2"];
//compare result
__block BOOL bol = (arr1.count == arr2.count);
// if count equal
if (bol) {
[arr1 sortedArrayUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2) {
if (![obj1 isEqualToString:obj2]) {
bol = NO;
}
return NSOrderedAscending;
}];
}

Reusable method to check if all objects are unequal using valueForKey

I have been working with this method for hours. It is supposed to simply check if the attribute of the cards passed in as otherCards isEqual to the same attribute of this (self) instance of class. But I am tearing my hair out - it is giving false results
(BOOL)otherCards: (NSArray *)otherCards allHaveUnequalAttribute: (NSString *)key
{
NSArray *values = [NSArray arrayWithArray:[otherCards valueForKey:key]];
int countUnequalMatches = 0;
for (NSNumber *setValue in values) {
if (![[self valueForKey:key] isEqual:setValue]) {
countUnequalMatches++;}
}
NSLog(#"countUnequalMatches %d values count: %d", countUnequalMatches, [values count]);
if (countUnequalMatches == [values count]) return YES ?: NO;
}
It is called like this:
if ([self otherCards:otherCards allHaveUnequalAttribute:CARD_SHAPE]) {
NSLog(#"All unequal");
} else {
NSLog(#"One or more card equal");
}
I found the bug myself
if (countUnequalMatches == [values count]) return YES ?: NO;
should be:
if (countUnequalMatches == [otherCards count]) return YES ?: NO;
as I used the Set as an way to count unique items.
This is the sort of thing that predicates are very well suited to handle.
-(BOOL)otherCards: (NSArray *)otherCards allHaveUnequalAttribute: (NSString *)key
{
NSPredicate *pred = [NSPredicate predicateWithFormat:#"%K == %#", key, [self valueForKey:key]];
NSArray *equalCards = [otherCards filteredArrayUsingPredicate:pred];
return equalCards.count == 0;
}

CALayerArray was mutated while being enumerated

I have to delete an object of an array every now and then, when I do it I get this error.
Collection < CALayerArray: 0xc4f3b20> was mutated while being enumerated
The error appears on this method, which is the accesor of the Array:
- (NSArray *)occupantsArray
{
if (dispatch_get_current_queue() == moduleQueue)
{
return occupantsArray;
}
else
{
__block NSArray *result;
dispatch_sync(moduleQueue, ^{ //ON THIS LINE
result = [occupantsArray copy];
});
return [result autorelease];
}
}
As you can see Im taking care of not returning the original array but a copy, but it still crashes.
Also the method where Im deleting elements of the array is this.
- (void)eraseJIDFromArray:(NSString*)jid{
dispatch_block_t block = ^{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int i = 0;
for(NSString *jidAct in occupantsArray){
if([jidAct isEqualToString:jid]){
[occupantsArray removeObjectAtIndex:i];
}
i++;
}
[pool drain];
};
if (dispatch_get_current_queue() == moduleQueue)
block();
else
dispatch_async(moduleQueue, block);
}
The array can have upto 200 elements, so it can take some time to go through all of them, but I'm setting queues, dont know what else I can do.
Any ideas?
Thanks.
This code:
for(NSString *jidAct in occupantsArray){
if([jidAct isEqualToString:jid]){
[occupantsArray removeObjectAtIndex:i];
}
i++;
}
is probably causing your problems. You shouldn't be removing elements while enumerating the array. The way you avoid this is by using an NSMutableIndexSet:
NSMutableIndexSet *indexes = [NSMutableIndexSet set]; // assuming NSInteger i;
for(NSString *jidAct in occupantsArray){
if([jidAct isEqualToString:jid]){
[indexes addIndex:i];
}
i++;
}
[occupantsArray removeObjectsAtIndexes:indexes];

View-based NSTableView filtering + animation

I have a view based NSTableView that I sometimes filter using NSPredicate. Is there any way to animate the items being removed/added/reordered throughout the tableview to have the same effect as beginUpdates, endUpdates and insertRowsAtIndexes:withAnimation, etc?
I've explored ways such as manually filtering out my array but my attempts proved to be futile so now I am wondering if there is a better (or built in way) to do this. I have wondered if NSArrayController does this automatically but I don't think it does.
I've written code to do this myself - given 'before' and 'after' arrays, compute the required parameters to insertRowsAtIndexPaths:, deleteRowsAtIndexPaths:, etc. The code is a bit fiddly so probably has bugs - use at your discretion!
#interface NSArray (ArrayDifference)
- (void) computeDifferenceTo:(NSArray *)newArray returningAdded:(NSMutableArray **)rowsAdded andDeleted:(NSMutableArray **)rowsDeleted;
#end
#implementation NSArray (ArrayDifference)
// Given two arrays that are expected have items added or removed but not re-ordered, compute the differences
// in a way usable for UITable insertRows and deleteRows
- (void) computeDifferenceTo:(NSArray *)newArray returningAdded:(NSMutableArray **)rowsAdded andDeleted:(NSMutableArray **)rowsDeleted
{
NSArray *oldArray = self;
*rowsAdded = [[[NSMutableArray alloc] init] autorelease];
*rowsDeleted = [[[NSMutableArray alloc] init] autorelease];
NSUInteger oldCount = [oldArray count];
NSUInteger newCount = [newArray count];
// Step through the two arrays
NSInteger oldIndex = 0, newIndex=0;
for (; newIndex < newCount && oldIndex < oldCount; )
{
id newItem = [newArray objectAtIndex:newIndex];
id oldItem = [oldArray objectAtIndex:oldIndex];
// If the two objects match, we step forward on both sides
if (newItem == oldItem) {
++newIndex;
++oldIndex;
}
else {
// Look for the old item to appear later in the new array, which would mean we have to add the rows in between
NSRange range = { newIndex+1, newCount - newIndex-1 };
NSUInteger foundIndex = [newArray indexOfObject:oldItem inRange:range];
if (foundIndex != NSNotFound)
for (; newIndex < foundIndex; ++newIndex)
[*rowsAdded addObject:[NSIndexPath indexPathForRow:newIndex inSection:0]];
else {
// Look for the new item to appear later in the old array, which would mean we have to remove the rows in between
NSRange range = { oldIndex+1, oldCount - oldIndex-1 };
NSUInteger foundIndex = [oldArray indexOfObject:newItem inRange:range];
if (foundIndex != NSNotFound)
for (; oldIndex < foundIndex; ++oldIndex)
[*rowsDeleted addObject:[NSIndexPath indexPathForRow:oldIndex inSection:0]];
else {
// Old item must be removed and new item added, then we carry on
[*rowsAdded addObject:[NSIndexPath indexPathForRow:newIndex++ inSection:0]];
[*rowsDeleted addObject:[NSIndexPath indexPathForRow:oldIndex++ inSection:0]];
}
}
}
}
// Once the loop is finished, add in what's left in the new array and remove what is left in the old array
for (; newIndex < newCount; ++newIndex)
[*rowsAdded addObject:[NSIndexPath indexPathForRow:newIndex inSection:0]];
for (; oldIndex < oldCount; ++oldIndex)
[*rowsDeleted addObject:[NSIndexPath indexPathForRow:oldIndex inSection:0]];
}
#end
Then you call it like this:
NSMutableArray *rowsAdded=nil, *rowsDeleted=nil;
[myArray computeDifferenceTo:newArray returningAdded:&rowsAdded andDeleted:&rowsDeleted];
[myTableView beginUpdates];
[myTableView insertRowsAtIndexPaths:rowsAdded withRowAnimation:UITableViewRowAnimationBottom];
[myTableView deleteRowsAtIndexPaths:rowsDeleted withRowAnimation:UITableViewRowAnimationFade];
[myTableView endUpdates];

How to move an item on NSMutableArray?

I want to move a string item to the top of the list.
NSMutableArray animal = "cat", "lion", "dog", "tiger";
How do I move dog to top of the list?
You would remove the item and insert it at the correct space:
id tmp=[[animals objectAtIndex:2] retain];
[animals removeObjectAtIndex:2];
[animals insertObject:tmp atIndex:0];
[tmp release];
You have to retain the object or when you tell the array to remove it, it will release the object.
If you don't know the index you could do something like this:
NSMutableArray* animals = [NSMutableArray arrayWithObjects:#"cat", #"lion", #"dog", #"tiger",nil];
for (NSString* obj in [[animals copy] autorelease]) {
if ([obj isEqualToString:#"dog"]) {
NSString* tmp = [obj retain];
[animals removeObject:tmp];
[animals insertObject:tmp atIndex:0];
break;
}
}
This method will go over all your list and search for "dog" and if it finds it will remove it from the original list and move it to index 0.
I would like to point out an inefficiency in your method. By removing/inserting an object from a NSMutableArray you potentially affect every row after the deletion/insertion. I say ‘potentially’ because it’s not clear what internal method Apple uses to maintain their mutable arrays. However, assuming it’s a simple c-array, then every row after that deletion/insertion index will be need to be moved down/up. In a very large array, this could be inefficient if the items moved are at the beginning. However, replacing items in an array are not inefficient at all. Thus the following is a category on NSMutableArray (note this code is under ARC, so no memory management):
- (void) moveObjectAtIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex{
if (fromIndex == toIndex) return;
if (fromIndex >= self.count) return; //there is no object to move, return
if (toIndex >= self.count) toIndex = self.count - 1; //toIndex too large, assume a move to end
id movingObject = [self objectAtIndex:fromIndex];
if (fromIndex < toIndex){
for (int i = fromIndex; i <= toIndex; i++){
[self replaceObjectAtIndex:i withObject:(i == toIndex) ? movingObject : [self objectAtIndex:i + 1]];
}
} else {
id cObject;
id prevObject;
for (int i = toIndex; i <= fromIndex; i++){
cObject = [self objectAtIndex:i];
[self replaceObjectAtIndex:i withObject:(i == toIndex) ? movingObject : prevObject];
prevObject = cObject;
}
}
}
Also, a small bonus to further increase functionality, if you're performing operations on the items moved (like updating a db or something), the following code has been very useful to me:
- (void) moveObjectAtIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex withBlock:(void (^)(id, NSUInteger))block{
if (fromIndex == toIndex) return;
if (fromIndex >= self.count) return; //there is no object to move, return
if (toIndex >= self.count) toIndex = self.count - 1; //toIndex too large, assume a move to end
id movingObject = [self objectAtIndex:fromIndex];
id replacementObject;
if (fromIndex < toIndex){
for (int i = fromIndex; i <= toIndex; i++){
replacementObject = (i == toIndex) ? movingObject : [self objectAtIndex:i + 1];
[self replaceObjectAtIndex:i withObject:replacementObject];
if (block) block(replacementObject, i);
}
} else {
id cObject;
id prevObject;
for (int i = toIndex; i <= fromIndex; i++){
cObject = [self objectAtIndex:i];
replacementObject = (i == toIndex) ? movingObject : prevObject;
[self replaceObjectAtIndex:i withObject:replacementObject];
prevObject = cObject;
if (block) block(replacementObject, i);
}
}
}
You can remove an existing element, e.g dog and then reinsert it at the beginning of the array.
NSMutableArray *animals = [NSMutableArray arrayWithObjects:#"cat", #"lion", #"dog", #"tiger",nil];
NSString *dog = #"dog";
// Check to see if dog is in animals
if ( [animals containsObject:dog] ) {
// Remove dog from animals and reinsert
// at the beginning of animals
[animals removeObject:dog];
[animals insertObject:dog atIndex:0];
}