In my app I use the following code to set a value of an array to zero:
[[records objectAtIndex:prizenotify] setValue:nil forKey:#"marbles"];
NSError *error;
[_managedObjectContext save:&error]; //Save
But, It crashes my code. Am I coding this correctly? Marbles is an NSNumber.
Thanks!
[[records objectAtIndex:prizenotify] setValue:[NSNumber numberWithInteger:0] forKey:#"marbles"];
Or in modern Objective-C:
[records[prizenotify] setValue:#0 forKey:#"marbles"];
If you're looking to set a nil-esque value, you can use [NSNull null] to insert null/nil values into a collection class like NSArray.
Although I would choose Sean's solution ([NSNull null]) as the simplest solution, there is also the NSPointerArray class (which specifically answers your question). Here's the overview:
NSPointerArray is a mutable collection modeled after NSArray but it
can also hold NULL values, which can be inserted or extracted (and
which contribute to the object’s count). Moreover, unlike traditional
arrays, you can set the count of the array directly. In a garbage
collected environment, if you specify a zeroing weak memory
configuration, if an element is collected it is replaced by a NULL
value.
Make sure that [records objectAtIndex:prizenotify] object is a NSMutableDictionary.
To be sure, test :
[[((NSDictionary*)[records objectAtIndex:prizenotify]) mutableCopy] setValue:[NSNumber numberWithInteger:0] forKey:#"marbles"];
if this one works, the problem is that you just have a NSDictionary (not mutable).
If you have a NSMutableDictionary for sure :
If you want to set the value to zero, call :
[[records objectAtIndex:prizenotify] setValue:[NSNumber numberWithInteger:0] forKey:#"marbles"];
If you want to delete the 'marbles' key from the dictionary, call :
[[records objectAtIndex:prizenotify] removeObjectForKey:#"marbles"];
Related
This the snippet of code where I get an error:
//sourceArray is a NSMutablearray
NSMutableDictionary *dayOfWeekDictionary= [sourceArray objectAtIndex:indexpath.row];
[dayOfWeekDictionary setObject:[NSNumber numberWithBool:YES] forKey:#"isSelected"];//line 2
What I’ve come to know from googling is that there is some stuff in assigning immutable object into mutable object.
I get an error at line 2. Any suggestions?
If [sourceArray objectAtIndex:indexpath.row] returns an immutable dictionary, simply assigning it to a variable of type NSMutableDictionary * doesn’t automatically convert it to a mutable dictionary. You could write this instead:
NSMutableDictionary *dayOfWeekDictionary= [NSMutableDictionary dictionaryWithDictionary:[sourceArray objectAtIndex:indexpath.row]];
By using +[NSMutableDictionary dictionaryWithDictionary:], you get a mutable dictionary based on another dictionary (which can be either mutable or immutable). Note that you do not own this dictionary, hence you don’t have to release it. Also note that this is not the same dictionary object as the one stored in the array. If you need both the array and dayOfWeekDictionary to be the same dictionary, then you should add a mutable dictionary to the array.
Are you sure there is an instance of NSMutableDictionary in [sourceArray objectAtIndex:indexpath.row]?
I suggest placing a break point at line 2 and inspecting the content of dayOfWeekDictionary.
AND
In your comment, you set an instance of NSMutableArray into the variable dayOfWeekDictionary of type NSMutableDictionary, if you try to call [dayOfWeekDictionary setObject:[NSNumber numberWithBool:YES] forKey:#"isSelected"]; the application will crash with an unrecognized selector sent to instance.
I create a custom object that has some properties like ID and Title,description etc...
And I add it to an array. (That array may contains more than 500 values).
And I use the following code to retrieve custom objects,
-(CustomObjects *)getObjectWithId:(int)id {
CustomObjects *objCustomObjects = nil;
for (CustomObjects *customObjects in arrayCustomObjects) {
if (customObjects.id == id) {
objCustomObjects = customObjects;
break;
}
}
return objCustomObjects;
}
But It has some performance problem, because I use the function to call on UIScrollview pinch.
How can I improve performance in fetching the objects?
thanks in advance,
A dictionary is better for this. The only catch is that you can’t have a NSDictionary with primitive int keys, so that you have to wrap the id in an NSNumber.
- (void) addCustomObject: (CustomObject*) obj {
NSNumber *wrappedID = [NSNumber numberWithInt:[obj idNumber]];
[dictionary setObject:obj forKey:wrappedID];
}
- (CustomObject*) findObjectByID: (int) idNumber {
NSNumber *wrappedID = [NSNumber numberWithInt:[obj idNumber]];
return [dictionary objectForKey:wrappedID];
}
A dictionary (also called hash table) does not have to go through all the values to find the right one, it has all the values arranged cleverly according to the keys so that it can jump to the right one or close to it. What you are doing with the array is called linear search and it’s not very efficient.
Better you can use NSDictionary with id as the key. You can easily fetch the object from the dictionary.
Is it Ok for your requirement?
You could use an NSPredicate that checks whether id equals the one you're looking for, and simply filter the custom objects using this predicate by calling filteredArrayUsingPredicate:.
To improve performance, I would try to postpone whatever you're trying to calculate by not directly calling the function that does the heavy work in your scroll view, but rather call [self performSelector:... withObject:nil afterDelay:0]; which postpones the calculation to the next runloop cycle. If you check if there's already a calculation scheduled before you call performSelector you should actually be able to reduce the frequency of the calculation while maintaining a crisp interface.
You must ditch the array in favor for a dictionary if you want to have fast lookups.
If you want to access objects both by key and index then you need to the objects around in two collections, and make sure they are in sync.
I have already done a helper class for this named CWOrderedDictionary. It's a subclass of NSMutableDictionary that allows for access to objects by both keys (as any dictionary do), and by index using methods identical to NSMutableArray.
My class is available to use for inspiration or as is from here: https://github.com/jayway/CWFoundation/
Use NSPredicate:-
You will receive the filtered array with the object that has the id you passed;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"id == %#", id];
NSArray *filtered = [arrayCustomObjects filteredArrayUsingPredicate:predicate];
Instead of intjust use [NSNumber numberWithInt:] , i did some changes in your given code.
-(CustomObjects *)getObjectWithId:(NSNumber* )id {//changed int to NSNumber
CustomObjects *objCustomObjects = nil;
NSPredicate *bPredicate = [NSPredicate predicateWithFormat:#"SELF.id==%#",id];
NSArray *result = [array filteredArrayUsingPredicate:bPredicate];
//return filtered array contains the object of your given value
if([result count]>0)
objCustomObjects = [result objectAtIndex:0];
}
return objCustomObjects;
}
dict is NSMutableDictionary; array is NSMutableArray and is NOT nil;
The code:
[dict setObject:array forKey:#"key"];
There is no error or warning but the dict is null,nothing is set or added.
When I use the code below,it works:
[dict setObject:[NSArray arrayWithArray:array] forKey:#"Key"];
Can anyone tell me why ?
Update:
Both dict and array are local variables and have been initialized.The dict is nil.
NSLog(#"%#", array) has printed the value of array:
({"Title":"firstTitle","Date":"20110101"},{"Title":"secondTitle","Date":"20110102"})
UPDATE:
I have made a mistake.The array is not null at first,but I emptied it in follow operation.
Thanks for #Bavarious.
You're aware that you're using different capitalizations of "key", right? #"Key" is not the same thing as #"key".
I think you have not initialized array in 1st statement. check if its allocated memory or not?
The code seems to be correct. Most probably your array is nil.
When using arrayWithArray, the inserted object will be an array, even if its value itself is set to nil.
In my iphone app i have implemented the SearchBar on TableView.
Now the SearchBar Searches the items but the searched items are not shown in the tableview as the NSMutableArray which fills up the table with the search results is not retaining the values.
I have put the screenshot of the code and the NSLog statements for the count of copyListOfItems always return 0 even though the NSLog(#"%#",sTemp); shows the searched items in Console.
I have created the property for the NSMutableArray copyListOfItems and also synthesized it but its count in the Console is always shown as Zero.
Here is the Code:
searchArray = [Food mutableCopy];
for (NSString *sTemp in searchArray)
{
NSLog(#"Value: %#",NSStringFromClass([sTemp class]));
NSRange titleResultsRange = [sTemp rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (titleResultsRange.length > 0)
{
NSLog(#"sTemp is:%#",sTemp);
NSLog(#" is:%#",sTemp);
[copyListOfItems addObject:sTemp];
[copyListOfItems retain];
}
NSLog(#"Copy list count in Search Delegate Method inside For : %d",[copyListOfItems count]);
}
NSLog(#"Copy list count in Search Delegate Method : %d",[copyListOfItems count]);
[searchArray release];
searchArray = nil;
searching = YES;
[self.table reloadData];
de:
What should I do?
Please Help
Please Suggest
Thanks
From what you've said, it sounds as though the copyListOfItems array is never created. Synthesizing the property creates a pair of accessor methods, but doesn't automatically create an instance of NSMutableArray for you.
So somewhere, you'll need to call [[NSMutableArray alloc] init] (or some variant of init...), and assign the result to the instance variable, or call [NSMutableArray array] (or a variant of array...), and pass the result as an argument to the setCopyListOfItems: method.
I can only assume that you're over-releasing the items in the array. It appears that you do not have a good grip on the retain/release concept, since you're over-retaining the NSArray copyListOfItems. You shouldn't be calling -retain on the same object within a loop since you're incrementing a single object's retain count by 1 for each iteration of the loop.
That aside, what does this code output in its NSLog() calls? It doesn't make a lot of sense. Why do you try to take a mutable copy of (what appears to be) a class called Food?
What exactly is "Food"? An objective-C object, or an objective-C class? This should not be compiling at all in my opinion, unless it's just very badly named.
Make sure you always alloc init the NSMutableArray. If you don't do that, all the items you add will just euhm.. disappear. So make sure you do this somewhere:
searchArray = [[NSMutableArray alloc] init];
Oops..!!!! Cleaned my project and build and run again and it has started working.Thanks guys for being so helpful.. :]
where are you creating the copyListOfItems Array?
I guess copyListOfItems is nil. And you are trying to add something to nil array and trying to retain the nil.
Note: whatever the operation you do on the nil will not be affected. Check whether you are creating the copyListOfItems array properly.
And why are you trying to retain the same array after adding each object?
I'm assuming my understanding of how to perform a deep copy isn't just there yet. The same with some sub-optimal memory handling that I'm performing down below. This code below probably depicts a shallow copy, and I believe that's where my problem might be. I have some cookie-cutter code for an example that looks like the following:
NSArray *user = [[xmlParser createArrayWithDictionaries:dataAsXML
withXPath:kUserXPath] retain];
if([user count] > 0) {
self.name = [[user valueForKey:#"name"] copy];
}
// Crash happens if I leave the next line un-commented.
// But then we have a memory leak.
[user release];
[xmlParser release];
Unfortunately when I comment out [user release], the code works, but we have an obvious memory leak. The method createArrayWithDictionaries:withXPath: was refactored last night when the SO community helped me understand better memory management. Here's what it looks like:
- (NSArray *)createArrayWithDictionaries:(NSString *)xmlDocument
withXPath:(NSString *)XPathStr {
NSError *theError = nil;
NSMutableArray *dictionaries = [NSMutableArray array];
CXMLDocument *theXMLDocument = [CXMLDocument alloc];
theXMLDocument = [theXMLDocument initWithXMLString:xmlDocument
options:0
error:&theError];
NSArray *nodes = [theXMLDocument nodesForXPath:XPathStr error:&theError];
for (CXMLElement *xmlElement in nodes) {
NSArray *attributes = [xmlElement attributes];
NSMutableDictionary *attributeDictionary;
attributeDictionary = [NSMutableDictionary dictionary];
for (CXMLNode *attribute in attributes) {
[attributeDictionary setObject:[attribute stringValue]
forKey:[attribute name]];
}
[dictionaries addObject:attributeDictionary];
}
[theXMLDocument release];
return dictionaries;
}
I'm guessing there's a couple of issues that might be going on here:
Auto release on my dictionaries array is happening, thus my app crashing.
I'm not performing a deep copy, only a shallow copy. Thus when the user array is released, self.name is done for.
With NSZombieEnabled, I see the following:
*** -[CFString respondsToSelector:]:
message sent to deallocated instance 0x1ae9a0
Also, the final call where the backtrace shows this is crashing contains the following code in a separate module from the other two methods:
User *u = self.user;
NSString *uri = [NSString stringWithFormat:#"%#/user/%#/%#",
[self groupName], u.userId, kLocationsUri];
Between all the auto releasing/copies/retain happening between the client code and createArrayWithDictionaries:withXPath, I'm a bit confused as to the real problem here. Thanks again for helping me understand.
OK, you don't need to retain the return value from createArrayWithDictionaries: since you're not keeping it around. The return value is autoreleased. I'd strongly recommend reading up on how autoreleasing works. You only retain things that you intend to keep around in your object.
Also, user is an NSArray. If you call [user valueForKey:#"name"], you'll get another NSArray of values representing the values of the name key for each of the objects in users. Furthermore, how is the name property on your object defined? If you declared it as copy or retain (I believe retain is the default if you don't specify it yourself), you don't need to copy or retain the value. Indeed, the accessor should always be responsible for doing the memory management, not the caller. If you wrote your own accessor (i.e. you didn't use the #synthesize keyword), you need to make sure you do the memory management there.
I'm guessing what you meant to write was something more like this:
NSArray *user = [xmlParser createArrayWithDictionaries:dataAsXML withXPath:kUserXPath];
if ([user count] > 0)
self.name = [[user objectAtIndex:0] objectForKey:#"name"];
[xmlParser release];
I think your troubles are stemming from a misunderstanding of how memory management works in Objective-C.
Hope this helps.
Auto release on my dictionaries array is happening, thus my app crashing.
If the caller intends to keep the array around somewhere, it needs to retain it. Otherwise, it will crash when it tries to access the (now-deceased) object.
If the caller is going to store it in a property, it must use the self.dictionaries = […] syntax, not dictionaries = […]. The former is a property access, which calls the setter method; the latter is a direct instance variable assignment.
Coming back to your actual question, that of a deep copy: You need to get the sub-elements of every element and put them in each element's dictionary.
Basically, you need a recursive method (or a queue, but that's harder—file under premature optimization until you've proven you need it) that takes an element and returns a dictionary, and then you need to call this method on each of your element's child elements, and collect the results into an array and put that into the dictionary you're creating.
I would recommend making this recursive method an instance method of the element. Something like:
- (NSDictionary *) dictionaryRepresentation {
NSMutableDictionary *attributeDictionary = [NSMutableDictionary dictionary];
for (CXMLNode *attribute in attributes) {
[attributeDictionary setObject:[attribute stringValue] forKey:[attribute name]];
}
NSArray *childElements = [self childElements];
return [NSDictionary dictionaryWithObjectsAndKeys:
attributeDictionary, #"attributes",
[childElements valueForKey:#"dictionaryRepresentation"], #"childElements",
nil];
}
Then you replace the loop in createArrayWithDictionaries:withXPath: with a similar valueForKey: message. I'll leave you to fill it in.
valueForKey: is Key-Value Coding's principal method. In both places, we're making use of NSArray's handy implementation of it.
(If the use of valueForKey: still doesn't make sense to you, you should read the KVC Programming Guide. KVC is vitally important in modern Cocoa, so you do need to read this sooner or later.)