while removing the runtime memory leaks in my iPad application , I came across this strange memory leak in NSObject+JSONSerializableSupport class in the following method
+ (id) deserializeJSON:(id)jsonObject {
id result = nil;
if ([jsonObject isKindOfClass:[NSArray class]]) {
//JSON array
result = [NSMutableArray array];
for (id childObject in jsonObject) {
[result addObject:[self deserializeJSON:childObject]];
}
}
else if ([jsonObject isKindOfClass:[NSDictionary class]]) {
//JSON object
//this assumes we are dealing with JSON in the form rails provides:
// {className : { property1 : value, property2 : {class2Name : {property 3 : value }}}}
NSString *objectName = [[(NSDictionary *)jsonObject allKeys] objectAtIndex:0];
Class objectClass = NSClassFromString([objectName toClassName]);
if (objectClass != nil) {
//classname matches, instantiate a new instance of the class and set it as the current parent object
result = [[[objectClass alloc] init] autorelease];
}
NSDictionary *properties = (NSDictionary *)[[(NSDictionary *)jsonObject allValues] objectAtIndex:0];
NSDictionary *objectPropertyNames = [objectClass propertyNamesAndTypes];
for (NSString *property in [properties allKeys]) {
NSString *propertyCamalized = [[self convertProperty:property andClassName:objectName] camelize];
if ([[objectPropertyNames allKeys]containsObject:propertyCamalized]) {
Class propertyClass = [self propertyClass:[objectPropertyNames objectForKey:propertyCamalized]];
[result setValue:[self deserializeJSON:[propertyClass deserialize:[properties objectForKey:property]]] forKey:propertyCamalized];
}
}
}
else {
//JSON value
result = jsonObject;
}
return result;
}
I am getting the memory leak on this line
[result setValue:[self deserializeJSON:[propertyClass deserialize:[properties objectForKey:property]]] forKey:propertyCamalized];
Please suggest a solution or tell me where i am going wrong.
Related
NSArray *pets = [NSArray arrayWithObjects:#"Cat", #"Dog", #"Rat", nil];
// how do I store int value 456 in this array after #"Rat" object, + pet is of type NSString so will not it generate error in while loop...??? so what data-type should i use for pet pointer that can represent all nextObject values/objects
NSEnumerator *enumerator = [pets objectEnumerator];
NSString *pet;
while (pet = [enumerator nextObject]) {
NSLog(#"Pet: %#", pet);
}
Since NSArray will only hold object you can not add an integer, you will need to wrap it in a NSNumber.
NSArray *pets = #[#"Cat", #"Dog", #"Rat", #456];
This will work with the loop you have in your example, but if you want to call any NSString method you will need to check the type:
for(id pet in pets) {
if(![pet isKindOfClass:[NSString class]) {
// It not a string, just continue to the next object.
continue;
}
}
Or a while loop:
id pet;
NSEnumerator *enumerator = [pets objectEnumerator];
while (pet = [enumerator nextObject]) {
if(![pet isKindOfClass:[NSString class]) {
// It not a string, just continue to the next object.
continue;
}
}
Use #456, it's the same as [NSNumber numberWithInt:456]
Use id. i.e.:
id pet;
while (pet = [enumerator nextObject]) {
NSLog(#"Pet: %#", pet); // maybe unsafe if only a subset of the objects in the array can execute the methods
// if you want to unwrap the pet to the concrete class, cast it with (NSString *)pet
// but make sure the object is of correct type
if ([pet isKindOfClass: [NSString class]]) {// use isMemberOfClass for exactly this class and isKindOfClasses for this class and subclasses of it
// here you are sure it is a NSString
}
}
I have a NSMutableDictionary and I want to swap values & keys. i.e, after swapping values becomes keys and its corresponding keys with become values All keys and values are unique. Looking for an in place solution because size is very big . Also, the keys and values are NSString objects
NSMutableDictionary *d = [NSMutableDictionary dictionaryWithDictionary:#{
#"key1" : #"value1",
#"key2" : #"value2"}];
for (NSString *key in [d allKeys]) {
d[d[key]] = key;
[d removeObjectForKey:key];
}
NSLog(#"%#", d); // => { value1 : key1,
// value2 : key2 }
Assumptions
unique values (as they will become keys)
values conform to NSCopying (same as above)
no value is equal to any key (otherwise colliding names will be lost in the process)
Here is another way to invert dictionary. The simplest for me.
NSArray *keys = dictionary.allKeys;
NSArray *values = [dictionary objectsForKeys:keys notFoundMarker:[NSNull null]];
[dictionary removeAllObjects]; // In case of huge data sets release the contents.
NSDictionary *invertedDictionary = [NSDictionary dictionaryWithObjects:keys forKeys:values];
[dictionary setDictionary:invertedDictionary]; // In case you want to use the original dictionary.
EDIT: I had written a few lines of codes to get the OP started into the task of creating his own algorithm. The answer was not well received so I have crafted a full implementation of an algorithm that does what he asks, and goes one step further.
Advantages:
Makes no assumptions regarding the contents of the dictionary, for example, the values need not conform to the 'NSCopying' protocol
Transverses the whole hierarchy of a collection, swapping all the keys
It's fast since it uses recursion and fast enumeration
Does not alter the contents of the original dictionary, it creates a brand new one
Code has been implemented through categories to both collections:
#interface NSDictionary (Swapping)
- (NSDictionary *)dictionaryBySwappingKeyWithValue;
#end
#interface NSDictionary (Swapping)
- (NSDictionary *)dictionaryBySwappingKeyWithValue
{
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithCapacity:self.count];
[self enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
id newKey = nil;
if ([value isKindOfClass:[NSDictionary class]]) {
newKey = [value dictionaryBySwappingKeyWithValue];
} else if ([value isKindOfClass:[NSArray class]]) {
newKey = [value arrayBySwappingKeyWithValue];
} else {
newKey = value;
}
if (![newKey conformsToProtocol:#protocol(NSCopying)]) {
newKey = [NSValue valueWithNonretainedObject:newKey];
}
mutableDictionary[newKey] = key;
}];
return [NSDictionary dictionaryWithDictionary:mutableDictionary];
}
#end
and...
#interface NSArray (Swapping)
- (NSArray *)arrayBySwappingKeyWithValue;
#end
#implementation NSArray (Swapping)
- (NSArray *)arrayBySwappingKeyWithValue
{
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:self.count];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[NSDictionary class]]) {
NSDictionary *newDict = [obj dictionaryBySwappingKeyWithValue];
mutableArray[idx] = newDict;
} else if ([obj isKindOfClass:[NSArray class]]) {
NSArray *newArray = [obj arrayBySwappingKeyWithValue];
mutableArray[idx] = newArray;
} else {
mutableArray[idx] = obj;
}
}];
return [NSArray arrayWithArray:mutableArray];
}
#end
As an example, assume you have a dictionary with the following structure:
UIView *view = [[UIView alloc] init];
NSDictionary *dict = #{#"1" : #"a",
#"2" : #[ #{ #"5" : #"b" } ],
#"3" : #{#"6" : #"c"},
#"7" : view};
NSDictionary *newDict = [dict dictionaryBySwappingKeyWithValue];
Printing the newDict object in the console will give you this output:
(lldb) po mutableDictionary
{
a = 1;
({b = 5;}) = 2;
{c = 6;} = 3;
"<30b50617>" = 7;
}
As you can see, not only have the keys and values been swapped at the first level of the hierarchy, but deep inside each collection.
"<30b50617>" represents the UIView object wrapped inside a NSValue. Since UIView does not comply to the NSCopying protocol, it needs to be handled this way if you want it to be a key in your collection.
Note: Code was done in a couple of minutes. Let me know if I missed something.
for (NSString *key in [myDictionary allKeys]) {
NSString *value = [responseDataDic objectForKey:key];
[myDictionary removeObjectForKey:key];
[myDictionary addObject:key forKey:value];
}
Assumption:
No key = value;
Complexity:
No extra space required. Will loop through once and replace all key value pairs.
NSArray* allKeys = [theDict allKeys];
NSArray* allValues = [theDict allValues];
NSMutableDictionary* newDict = [NSMutableDictionary dictionaryWithObjects:allKeys forKeys:allValues];
I have a problem with fetching data from Json response.
Here is an example data structure :
(
{
AT = "<null>";
DId = 0;
DO = 0;
PLId = 33997;
PdCatList = (
{
PLId = 33997;
PPCId = 0;
pdList = (
{
IsDis = 0;
IsPS = 0;
IsT = 1;
PA = 1;
PCId = 119777;
}
);
}
);
PdId = 0;
SId = 0;
Sec = 1;
},
{
AT = "<null>";
DId = 0;
DO = 11;
Dis = 0;
PLId = 34006;
PdCatList = (
{
PLId = 34006;
PPCId = 0;
pdList = (
{
IsDis = 0;
IsPS = 0;
IsT = 1;
PA = 1;
PCId = 119830;
},
{
IsDis = 0;
IsPS = 0;
IsT = 1;
PA = 1;
PCId = 119777;
}
);
},
{
PLId = 33997;
PPCId = 0;
pdList = (
{
IsDis = 0;
IsPS = 0;
IsT = 1;
PA = 1;
PCId = 119777;
}
);
}
);
PdId = 0;
SId = 0;
Sec = 1;
},
)
how would i parse the resulting Structure ?
I would like to get a list of values directly. What if i have several values in a tupel for example performer PdCatList, pdList. How would i access those values?
Can anyone help me
Thank's
my code is
NSError *error;
Array1 = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
for(int i=0;i<[Array1 count];i++)
{
NSDictionary *dict1 = [Array1 objectAtIndex:i];
NSLog(#"Array1.....%#",dict1);
Array2=[dict1 valueForKey:#"PdCatList"];
for(int i=0;i<[Array2 count];i++)
{
NSDictionary *dict2 = [Array2 objectAtIndex:i];
NSLog(#"Array2.....%#",dict2);
Array3=[dict2 valueForKey:#"pdList"];
for(int i=0;i<[Array3 count];i++)
{
NSDictionary *dict3 = [Array3 objectAtIndex:i];
NSLog(#"Array3.....%#",dict3);
}
}
}
Try this...
NSError *error;
Array1 = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
for(int i=0;i<[Array1 count];i++)
{
NSDictionary *dict1 = [Array1 objectAtIndex:i];
ATArray =[dict1 valueForKey:#"AT"];
DIdArray =[dict1 valueForKey:#"DId"];
DOArray =[dict1 valueForKey:#"DO"];
PLIdArray =[dict1 valueForKey:#"PLId"];
etc...
Array2=[dict1 valueForKey:#"PdCatList"];
for(int i=0;i<[Array2 count];i++)
{
NSDictionary *dict2 = [Array2 objectAtIndex:i];
PLIdArray =[dict2 valueForKey:#"PLId"];
PPCIdArray =[dict2 valueForKey:#"PPCId"];
etc…
Array3=[dict2 valueForKey:#"pdList"];
for(int i=0;i<[Array3 count];i++)
{
NSDictionary *dict3 = [Array3 objectAtIndex:i];
IsDisArray =[dict3 valueForKey:#"IsDis"];
IsPSArray =[dict3 valueForKey:#"IsPS"];
IsTArray =[dict3 valueForKey:#"IsT"];
PAArray =[dict3 valueForKey:#"PA"];
PCIdArray =[dict3 valueForKey:#"PCId"];
}
}
}
I think what you require here is to understand what a JSON response is rather than the Answer to get the values of some objects from your JSON response.
If you want some detail explanation about JSON Parsing then you can take a look at NSJSONSerialization Class Reference. Everything is given there or you can take a look at my Answer.
Understand the Concept. It depends on what you have inside your JSON. If it's an Array ( Values inside [ ]) then you have to save in NSArray, if it's a Dictionary ( Values inside { }) then save as NSDictionary and if you have single values like string , integer, double then you have to save them using appropriate Objective-C Data types.
For some simple details with example , you can check my Answer from this question.
Use JSONKit(https://github.com/johnezang/JSONKit):
NSString *yourJSONString = ...
NSArray *responseArray = [yourJSONString objectFromJSONString];
for(NSDictionary *responseDictionary in responseArray)
{
NSString *atString = [responseDictionary objectForKey:#"AT"];
...
NSArray *pdCatListArray = [responseDictionary objectForKey:#"PdCatList"];
...here you can get all values you want,if you want to get more details in PdCatList,use for in pdCatListArray ,you can do what you want.
}
Use following method:
NSDictionary *mainDict;
SBJSON *jsonParser = [[SBJSON alloc]init];
if([[jsonParser objectWithString:responseString] isKindOfClass:[NSDictionary class]])
{
mainDict=[[NSDictionary alloc]initWithDictionary:[jsonParser objectWithString:responseString]];
}
NSDictionary *firstDict=[NSDictionary alloc]initWithDictionary:[mainDict valueForKey:#""];
You have to add JSON framework which is parse string into NSDictionary.
Use zip file from here
Open Folder and rename Classes folder to "JSON".
Copy JSON Folder and include in your project.
Import header file like below in controller where you want to parse JSON String.
#import "SBJSON.h"
#import "NSString+SBJSON.h"
Now, Parse your response string in to NSDictionary like below.
NSMutableDictionary *dictResponse = [strResponse JSONValue];
You can use KVC
to access the nested properties in the JSON. You need to know about KVC and dot syntax and Collection operators
Frameworks that map JSON to objects, such as RestKit rely heavily on KVC.
Following your sample, you could get a list of all PdCatList objects:
//sample data
NSArray *json = #[
#{#"PLId" : #33997,
#"PdCatList" : #{#"PLId": #33998,
#"PPCId" : #1,
#"pdList" : #{
#"PCId" : #119777
}}
},
#{#"PLId" : #33999,
#"PdCatList" : #{#"PLId": #4444,
#"PPCId" : #0,
#"pdList" : #{
#"PCId" : #7777
}}}
];
//KVC
NSArray *pdCatLists = [json valueForKeyPath:#"#unionOfObjects.PdCatList"];
With this you can, for example, make a very basic object mapping (which does not take care of relationships)
In PdCatList.h
#interface PdCatList : NSObject
#property (readonly, strong, nonatomic) NSNumber *PLId;
#property (readonly, strong, nonatomic) NSNumber *PPCId;
+ (instancetype)listWithDictionary:(NSDictionary *)aDictionary;
#end
In PdCatList.m
#implementation PdCatList
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
#try {
[super setValue:value forUndefinedKey:key];
}
#catch (NSException *exception) {
NSLog(#"error setting undefined key: %#, exception: %#", key, exception);
};
}
+ (id)listWithDictionary:(NSDictionary *)aDictionary
{
PdCatList *result = [[self alloc] init];
[result setValuesForKeysWithDictionary:aDictionary];
return result;
}
#end
After getting the json object
NSArray *pdCatLists = [json valueForKeyPath:#"#unionOfObjects.PdCatList"];
[pdCatLists enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
PdCatList *each = [PdCatList listWithDictionary:obj];
}];
However, If what you want is just to flatten the json, you must use recursion and create a category similar to the following.
In NSJSONSerialization+FlattenedJSON.h
#interface NSJSONSerialization (FlattenedJSON)
+ (void)FlattenedJSONObjectWithData:(NSData *)data completionSuccessBlock:(void(^)(id aJson))onSuccess failure:(void(^)(NSError *anError))onFailure;
#end
In NSJSONSerialization+FlattenedJSON.m
#import "NSJSONSerialization+FlattenedJSON.h"
#implementation NSJSONSerialization (FlattenedJSON)
+ (void)FlattenedJSONObjectWithData:(NSData *)data completionSuccessBlock:(void (^)(id))onSuccess failure:(void (^)(NSError *))onFailure
{
NSError *error;
id object = [self JSONObjectWithData:data
options:kNilOptions
error:&error];
if (error)
{
onFailure(error);
}
else
{
NSMutableArray *result = [NSMutableArray array];
[self flatten:object
inArray:result];
onSuccess([result copy]);
}
}
+ (void)flatten:(id)anObject inArray:(NSMutableArray *)anArray
{
if ([anObject isKindOfClass:NSDictionary.class])
{
[((NSDictionary *)anObject) enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[self flatten:obj inArray:anArray];
}];
}
else if ([anObject isKindOfClass:NSArray.class])
{
[((NSArray *)anObject) enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[self flatten:obj inArray:anArray];
}];
}
else
{
[anArray addObject:anObject];
}
}
#end
I am parsing an itunes rss feed with JSON but I have run into a problem. The following code is running properly for one the movieName output but I still don't get the movieSummary output.
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
allDataDictionary = [NSJSONSerialization JSONObjectWithData:webData options:0 error:nil];
feed = [allDataDictionary objectForKey:#"feed"];
arrayOfEntry = [feed objectForKey:#"entry"];
for (NSDictionary *dictionTitle in arrayOfEntry) {
NSDictionary *title = [dictionTitle objectForKey:#"title"];
NSString *labelTitle = [title objectForKey:#"label"];
[arrayLable addObject:labelTitle];
NSDictionary *summary = [dictionTitle objectForKey:#"summary"];
NSString *labelSummary = [summary objectForKey:#"label"];
[arraySummary addObject:labelSummary];
}
movieName.text = [arrayLable objectAtIndex:0];
movieSummary.text = [arraySummary objectAtIndex:0]; //This is not displaying
}
Here is the link that I am parsing: http://itunes.apple.com/us/rss/topmovies/limit=300/json
I run into this situation a lot. I use something like this. Replace your code
NSString *labelTitle = [title objectForKey:#"label"];
[arrayLable addObject:labelTitle];
with
NSString * labelTitle = [ [ title objectForKey:#"label" ] ifNullThenNil ] ;
[ arrayLabel addObject:labelTitle ? labelTitle : #"" ] ; // you could also use #"<unknown>" or similar instead of #""
where -ifNullThenNil is provided via category:
#implementation NSObject (IfNullThenNil)
-(id)ifNullThenNil { return self ; }
#end
#implementation NSNull (IfNullThenNil)
-(id)ifNullThenNil { return nil ; }
#end
The problem was that when I was adding the strings to the Array that it sometimes contained NULL's thus the following code helped me out
if ([[arrayName objectAtIndex:0] isKindOfClass:[NSNull class]]) {
labelName.text = #"This is NULL";
} else {
[arrayName addObject:labelName];
}
if ([[arraySummary objectAtIndex:0] isKindOfClass:[NSNull class]]) {
labelSummary.text = #"This is NULL";
} else {
[arraySummary addObject:labelSummary];
}
I have an NSArray of names, I want to sort them alphabetically into a UITableView and separate them into sections.
I have a tagged section at the top, being section 0. I want the names sorted aplhabetically to come after that. So all names beginning with A get put into section 1, B into section 2, and so on.
I need to be able to somehow get the number of rows for each section, and then put the objects in each section.
How do I do this?
Here is a method for a category on NSArray to do grouping:
#interface NSArray (Grouping)
- (NSArray*) groupUsingFunction: (id (*)(id, void*)) function context: (void*) context;
#end
#implementation NSArray (Grouping)
- (NSArray*) groupUsingFunction: (id (*)(id, void*)) function context: (void*) context
{
NSArray* groupedArray = nil;
NSMutableDictionary* dictionary = [NSMutableDictionary new];
if (dictionary != nil)
{
for (id item in self)
{
id key = function(item, context);
if (key != nil)
{
NSMutableArray* array = [dictionary objectForKey: key];
if (array == nil) {
array = [NSMutableArray arrayWithObject: item];
if (array != nil) {
[dictionary setObject: array forKey: key];
}
} else {
[array addObject: item];
}
}
}
groupedArray = [NSMutableArray arrayWithArray: [dictionary allValues]];
[dictionary release];
}
return groupedArray;
}
#end
You can use it like this:
id GroupNameByFirstLetter(NSString* object, void* context)
{
return [object substringToIndex: 1];
}
NSInteger SortGroupedNamesByFirstLetter(id left, id right, void* context)
{
return [[left objectAtIndex: 0] characterAtIndex: 0] - [[right objectAtIndex: 0] characterAtIndex: 0];
}
NSMutableArray* names = [NSArray arrayWithObjects: #"Stefan", #"John", #"Alex",
#"Sue", #"Aura", #"Mikki", #"Michael", #"Joe", #"Steve", #"Mac", #"Fred",
#"Faye", #"Paul", nil];
// Group the names and then sort the groups and the contents of the groups.
groupedNames_ = [[names groupUsingFunction: GroupNameByFirstLetter context: nil] retain];
[groupedNames_ sortUsingFunction: SortGroupedNamesByFirstLetter context: nil];
for (NSUInteger i = 0; i < [groupedNames_ count]; i++) {
[[groupedNames_ objectAtIndex: i] sortUsingSelector: #selector(compare:)];
}
I modified St3fans answer to be a bit more modern and work with Blocks instead:
#interface NSArray (Grouping)
- (NSArray*) groupUsingBlock:(NSString* (^)(id object)) block;
#end
- (NSArray*) groupUsingBlock:(NSString* (^)(id object)) block
{
NSArray* groupedArray = nil;
NSMutableDictionary* dictionary = [NSMutableDictionary new];
if (dictionary != nil)
{
for (id item in self)
{
id key = block(item);
if (key != nil)
{
NSMutableArray* array = [dictionary objectForKey: key];
if (array == nil) {
array = [NSMutableArray arrayWithObject: item];
if (array != nil) {
[dictionary setObject: array forKey: key];
}
} else {
[array addObject: item];
}
}
}
groupedArray = [NSMutableArray arrayWithArray: [dictionary allValues]];
[dictionary release];
}
return groupedArray;
}
You can use it like this:
NSArray *grouped = [arrayToGroup groupUsingBlock:^NSString *(id object) {
return [object valueForKey:#"name"];
}];
You should probably create an array of arrays, one for each letter, and store your names that way. While you can use a single array for storage, there's no quick way to do the segmentation you're looking for. Sorting, sure, but not section-ization.