How to set all properties of an NSObject to nil without having to specify all the properties explicitly.? - objective-c

I have a singleton object which has some properties and I would like to set all these values to nil or "NO" based on the type of properties. One way to do it is to write a reset method in which I set all these properties to nil myself like below..
This may even have instance variables which are also to be cleared.
-(void)reset
{
//Properties
self.lastLoggedInUser = nil;
self.localMasterDownload = NO;
self.isFirstLaunchDone = NO;
self.lastArchvingDate = nil;
self.archivingDueDate = nil;
self.dbEncryptionKey = nil;
self.checkInDone = NO;
//Instance variables
_isPostionModuleExtandedMode = NO;
_isPassengerModuleExtandedMode = NO;
}
But I am looking for a more generic and efficient method of doing this..
Any help is appreciated.
Thanks.

This gets you your classes properties, from there you just have to convert the key string into something that trys to nil the property. Its possible I think.
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
unsigned count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
for (int i = 0; i < count; i++) {
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
if([self valueForKey:key]!=nil){
[dict setObject:[self valueForKey:key] forKey:key];
}else{
[dict setObject:#NO forKey:key];
}
}
free(properties);
return [NSDictionary dictionaryWithDictionary:dict];

- (void) resetProperties {
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList([self class], &outCount);
for(i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName) {
NSString *propertyName = [NSString stringWithCString:propName encoding:[NSString defaultCStringEncoding]];
id value = [self valueForKey:propertyName];
NSLog(#"[berore]XXXresetProperties => [%#] %#=%#", [propertyName class],propertyName, value);
/* You can add some other checks */
if ([value isKindOfClass:[NSNumber class]]) {
[self setValue:[NSNumber numberWithInteger:0] forKeyPath:propertyName];
}
if ([value isKindOfClass:[NSString class]]) {
[self setValue:nil forKeyPath:propertyName];
}
if ([value isKindOfClass:[NSDictionary class]]) {
[self setValue:nil forKeyPath:propertyName];
}
id valueRes = [self valueForKey:propertyName];
NSLog(#"[after]XXXresetProperties => [%#] %#=%#\n---\n", [propertyName class],propertyName, valueRes);
}
}
free(properties);
}
You can get it there:
https://github.com/energy6x6/iOS-clear-all-property-in-class

Related

IOS::How to get the contact number from ABAddressBook

Phone number getting like this.
Phone ABMultiValueRef 0x17674380 with 1 value(s)
0: $!!$ (0x176740e0) - 7124779070 (0x176742a0)
How to get this number"7124779070" from the above line.
I'm using this code for ios 7.Is it correct or wrong ,Please sugggest me.
int i;
ABAddressBookRef contactBook = ABAddressBookCreateWithOptions(NULL, NULL);
NSMutableArray *allData = ( NSMutableArray *)(ABAddressBookCopyArrayOfAllPeople(contactBook));
CFIndex contactNum = CFArrayGetCount((__bridge CFArrayRef)(allData));
for (i = 0; i < contactNum; i++)
{
ABRecordRef ref = CFArrayGetValueAtIndex((__bridge CFMutableArrayRef)(allData), i);
NSString* firstName = ABRecordCopyValue(ref, kABPersonFirstNameProperty);
NSString* lastName = ABRecordCopyValue(ref, kABPersonLastNameProperty);
NSString* phonesNum = ABRecordCopyValue(ref, kABPersonPhoneProperty);
// Remove all formatting symbols that might be in both phone number being compared
NSCharacterSet *toExclude = [NSCharacterSet characterSetWithCharactersInString:#"/.()- "];
phonesNum = [[phonesNum componentsSeparatedByCharactersInSet:toExclude] componentsJoinedByString: #""];
NSString *phoneNumber = [[phonesNum componentsSeparatedByCharactersInSet:toExclude] componentsJoinedByString: #""];
NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
if (firstName!=nil)
{
[dic setObject:(__bridge id)(firstName) forKey:#"firstName"];
}
if (lastName !=nil) {
[dic setObject:(__bridge id)(lastName) forKey:#"lastName"];
}
if (phonesNum!=nil) {
[dic setObject:(__bridge id)(phonesNum) forKey:#"phonesNum"];
}
[arr_Contacts addObject:dic];
NSLog(#"First name %#", firstName);
NSLog(#"Last Name %#", lastName);
NSLog(#"Phone %#", phonesNum);
}
First, request permission:
ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();
if (status != kABAuthorizationStatusAuthorized && status != kABAuthorizationStatusNotDetermined) {
// tell user to enable contacts in privacy settings
NSLog(#"You previously denied access: You must enable access to contacts in settings");
return;
}
CFErrorRef error = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
if (!addressBook) {
NSLog(#"ABAddressBookCreateWithOptions error: %#", CFBridgingRelease(error));
return;
}
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
if (error) {
NSLog(#"ABAddressBookRequestAccessWithCompletion error: %#", CFBridgingRelease(error));
}
if (granted) {
[self getContacts:addressBook];
} else {
// tell user to enable contacts in privacy settings
NSLog(#"You just denied access: You must enable access to contacts in settings");
}
CFRelease(addressBook);
});
Second, to retrieve the phone numbers, use ABMultiValueCopyValueAtIndex:
- (void)getContacts:(ABAddressBookRef)addressBook
{
NSArray *allData = CFBridgingRelease(ABAddressBookCopyArrayOfAllPeople(addressBook));
NSInteger contactCount = [allData count];
for (int i = 0; i < contactCount; i++) {
ABRecordRef person = CFArrayGetValueAtIndex((__bridge CFArrayRef)allData, i);
NSString *firstName = CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty));
NSString *lastName = CFBridgingRelease(ABRecordCopyValue(person, kABPersonLastNameProperty));
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
if (firstName) {
dictionary[#"firstName"] = firstName;
}
if (lastName) {
dictionary[#"lastName"] = lastName;
}
ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
CFIndex phoneNumberCount = ABMultiValueGetCount(phones);
if (phoneNumberCount > 0) {
NSString *phone = CFBridgingRelease(ABMultiValueCopyValueAtIndex(phones, 0));
dictionary[#"phone"] = phone;
}
// or if you wanted to iterate through all of them, you could do something like:
//
// for (int j = 0; j < phoneNumberCount; j++) {
// NSString *phone = CFBridgingRelease(ABMultiValueCopyValueAtIndex(phones, j));
//
// // do something with `phone`
// }
if (phones) {
CFRelease(phones);
}
[arr_Contacts addObject:dictionary];
}
}
A couple of additional issues addressed above:
The ABAddressBookCreateWithOptions does not return a mutable array. It's an immutable array. Replace all of those mutable references with immutable.
You must honor the Create Rule, namely that you're responsible for releasing any object returned from a Core Foundation method with either Create or Copy in its name. If the object supports toll-free bridging (e.g. the array of contacts, the first name string, the last name string, etc.), you can transfer ownership to ARC with CFBridgingRelease or __bridge_transfer. If the object doesn't support toll-free bridging (such as the phones or addressBook objects, above), then you must explicitly call CFRelease for the object in question.
Make sure to run your code through the static analyzer (shift+command+B, or choose "Analyze" from Xcode's "Product" menu), and it will identify these sorts of issues for you.
If a function, such as ABAddressBookCreateWithOptions offers an error parameter, you should avail yourself of it. I illustrate the proper use of the CFErrorRef object above.
What do you recive in console from Phone %# NSLog ? if Phone ABMultiValueRef 0x17674380 with 1 value(s) 0: $!!$ (0x176740e0) - 7124779070 (0x176742a0)just substring after '-'
NSString *myString = #"Phone ABMultiValueRef 0x17674380 with 1 value(s) 0: $!!$ (0x176740e0) - 7124779070 (0x176742a0)";
NSArray *myArray = [myString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"-"]];
and this is my method to get phone's
- (void) getContacts
{
NSMutableDictionary *response = [[NSMutableDictionary alloc] init];
ABAddressBookRef contactBook = ABAddressBookCreateWithOptions(NULL, NULL);
arr_Contacts = [[NSMutableArray alloc] init];
ABAddressBookRef allPeople = contactBook;
CFArrayRef allContacts = ABAddressBookCopyArrayOfAllPeople(allPeople);
CFIndex numberOfContacts = ABAddressBookGetPersonCount(allPeople);
NSLog(#"contact == %#",allContacts);
NSLog(#"numberOfContacts------------------------------------%ld",numberOfContacts);
for(int i = 0; i < numberOfContacts; i++){
NSString* name = #"";
NSString* phone = #"";
NSString* email = #"";
ABRecordRef aPerson = CFArrayGetValueAtIndex(allContacts, i);
ABMultiValueRef fnameProperty = ABRecordCopyValue(aPerson, kABPersonFirstNameProperty);
ABMultiValueRef lnameProperty = ABRecordCopyValue(aPerson, kABPersonLastNameProperty);
ABMultiValueRef phoneProperty = ABRecordCopyValue(aPerson, kABPersonPhoneProperty);\
ABMultiValueRef emailProperty = ABRecordCopyValue(aPerson, kABPersonEmailProperty);
NSArray *emailArray = (__bridge NSArray *)ABMultiValueCopyArrayOfAllValues(emailProperty);
NSArray *phoneArray = (__bridge NSArray *)ABMultiValueCopyArrayOfAllValues(phoneProperty);
if (fnameProperty != nil) {
name = [NSString stringWithFormat:#"%#", fnameProperty];
}
if (lnameProperty != nil) {
name = [name stringByAppendingString:[NSString stringWithFormat:#" %#", lnameProperty]];
}
if ([phoneArray count] > 0) {
if ([phoneArray count] > 1) {
for (int i = 0; i < [phoneArray count]; i++) {
phone = [phone stringByAppendingString:[NSString stringWithFormat:#"%#, ", [phoneArray objectAtIndex:i]]];
}
}else {
phone = [NSString stringWithFormat:#"%#", [phoneArray objectAtIndex:0]];
}
}
if ([emailArray count] > 0) {
if ([emailArray count] > 1) {
for (int i = 0; i < [emailArray count]; i++) {
email = [email stringByAppendingString:[NSString stringWithFormat:#"%#\n", [emailArray objectAtIndex:i]]];
}
}else {
email = [NSString stringWithFormat:#"%#", [emailArray objectAtIndex:0]];
}
}
NSLog(#"NAME : %#",name);
NSLog(#"PHONE: %#",phone);
NSLog(#"EMAIL: %#",email);
NSLog(#"\n");
[response setObject:name forKey:#"name"];
[response setObject:phone forKey:#"phone"];
[response setObject:email forKey:#"email"];
[arr_Contacts addObject:response];
}
}
Cheers

Understanding a logic in update/add - NSUserDefaults (Objective C)

I'm writing a nice class to handle call block in NSUserDefaults.
Haven't tested it yet, but I think I miss something regarding update/add by callBlockID.
My Code:
My Code:
#implementation UserDefaultHandler
+ (UserDefaultHandler *)sharedInstance
{
static UserDefaultHandler *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[UserDefaultHandler alloc] init];
});
return instance;
}
-(void)updateOrAddCallBlock:(NSMutableDictionary*)callBlockObject
{
NSString *callBlockID = [callBlockObject objectForKey:#"callBlockID"];
BOOL needToUpdate = NO;
NSInteger indexDict = 0;
NSMutableArray *arrayOfAllCallBlock = [[NSMutableArray alloc] init];
arrayOfAllCallBlock = (NSMutableArray*)[[NSUserDefaults standardUserDefaults] objectForKey:#"arrayOfAllCallBlock"];
NSMutableDictionary *newDict = [[NSMutableDictionary alloc] init];
if (arrayOfAllCallBlock.count <= 0 || arrayOfAllCallBlock == nil)
{
[arrayOfAllCallBlock addObject:callBlockObject];
}
else
{
for (int i = 0; i < arrayOfAllCallBlock.count; i++)
{
NSMutableDictionary *mutableDict = [arrayOfAllCallBlock objectAtIndex:i];
NSString *idFromDict = [mutableDict objectForKey:#"callBlockID"];
if ([idFromDict isEqualToString:callBlockID])
{
needToUpdate = YES;
indexDict = i;
newDict = mutableDict;
}
}
if (needToUpdate)
{
[arrayOfAllCallBlock removeObjectAtIndex:indexDict];
[arrayOfAllCallBlock insertObject:newDict atIndex:indexDict];
}
else
{
[arrayOfAllCallBlock addObject:callBlockObject];
}
}
[self saveGlobalDict:arrayOfAllCallBlock];
}
- (NSMutableDictionary *)getCallBlockByID:(NSString*)callBlockID
{
NSMutableArray *arrayOfAllCallBlock = (NSMutableArray*)[[NSUserDefaults standardUserDefaults] objectForKey:#"arrayOfAllCallBlock"];
BOOL isFound = NO;
NSInteger indexDict = 0;
NSMutableDictionary *newDict = [[NSMutableDictionary alloc] init];
for (int i = 0; i < arrayOfAllCallBlock.count; i++)
{
NSMutableDictionary *mutableDict = [arrayOfAllCallBlock objectAtIndex:i];
NSString *idFromDict = [mutableDict objectForKey:#"callBlockID"];
if ([idFromDict isEqualToString:callBlockID])
{
isFound = YES;
indexDict = i;
newDict = mutableDict;
}
}
if (isFound)
{
return newDict;
}
else
{
return nil;
}
}
- (NSMutableArray*)getAllCallBlock
{
NSMutableArray *arrayOfAllCallBlock = (NSMutableArray*)[[NSUserDefaults standardUserDefaults] objectForKey:#"arrayOfAllCallBlock"];
return arrayOfAllCallBlock;
}
- (void)clearCallBlockDB
{
[[NSUserDefaults standardUserDefaults] removeObjectForKey:#"arrayOfAllCallBlock"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)saveGlobalDict:(NSMutableArray*)globalArray
{
self.globalArray = [[NSMutableArray alloc] init];
self.globalArray = globalArray;
[self clearCallBlockDB];
[[NSUserDefaults standardUserDefaults] setObject:self.globalArray forKey:#"arrayOfAllCallBlock"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)clearSingleCallBlock:(NSString*)callBlockID
{
NSMutableArray *array = [[NSMutableArray alloc] init];
array = [self getAllCallBlock];
NSMutableDictionary *specificDict = [[NSMutableDictionary alloc] init];
NSInteger getIndex = [self getIndexForCallBlock:array specificDict:specificDict];
[array removeObjectAtIndex:getIndex];
[self saveGlobalDict:array];
}
- (NSInteger)getIndexForCallBlock:(NSMutableArray*)arrayOfAllCallBlock specificDict:(NSMutableDictionary*)specificDict
{
NSInteger getIndex = 0;
for (int i = 0; i < arrayOfAllCallBlock.count; i++)
{
NSMutableDictionary *mutableDict = [arrayOfAllCallBlock objectAtIndex:i];
NSDictionary *dict_one = [specificDict copy];
NSDictionary *dict_two = [mutableDict copy];
if ([dict_two isEqualToDictionary:dict_one])
{
getIndex = i;
}
}
return getIndex;
}
#end
I'll explain: I've a UITableView and when I add a row to it, I've a new window to put details, save and present back in UITableView.
Also, I've an edit button so I can edit and update a row in the table.
Adding a row to table create a unique string (callBlockID).
I'm not sure how to handle it and whether or not my code is enough.
Any ideas?

Path for key in NSDictionary

In my app I need to insert values into NSDictionary having only last path component. E.g. my given dict is
a = 1;
b = 2;
c = 3;
d = {
e = 4;
f = 5;
g = {
h = 6;
};
};
j = {
k = 7;
};
and I need to change value for k. Path components should be #[#"j", #"k"]. I tried it with something like that:
- (void)recurse:(NSDictionary*)dict keyToFound:(NSString*)ktf stack:(NSMutableArray*)stack parent:(NSString*)parent
{
for (NSString *key in [dict allKeys]) {
if ([key isEqualToString:ktf]) {
[stack insertObject:key atIndex:[stack count]];
return;
}
else {
if ([[dict valueForKey:key] isKindOfClass:[NSDictionary class]]) {
NSDictionary *d = [dict valueForKey:key];
[stack insertObject:key atIndex:[stack count]];
[self recurse:d keyToFound:ktf stack:stack parent:key];
//[stack removeObject:key];
}
}
}
}
but, obviuos, it's a wrong way.
Here are two simpler ways to set the value in nested dictionaries. The first assumes that you know that the keys exist and could be C identifiers. In this case using setValue:forKeyPath: is the best approach. Otherwise a simple loop in function (or a Method or a category on NSDictionary) will do the trick:
void setValueForPathComponentsOfDictionary(id value, NSArray *components, NSMutableDictionary *dict) {
NSMutableArray *parts = [components mutableCopy];
id lastPart = parts.lastObject;
[parts removeLastObject];
for (id part in parts) {
if (![dict respondsToSelector:#selector(objectForKey:)])
return; // Silently fail.
dict = [dict objectForKey:part];
}
if ([dict respondsToSelector:#selector(objectForKey:)])
[dict setValue:value forKey:lastPart];
}
int main (int argc, const char * argv[])
{
#autoreleasepool {
NSMutableDictionary *dict = [#{
#"a":[#{#"a":#1,#"b":#2} mutableCopy],
#"b":[#{#"a":#3,#"b":#4} mutableCopy]
} mutableCopy];
NSLog(#"Initial Dictionary: %#", dict);
[dict setValue:#9 forKeyPath:#"a.b"];
NSLog(#"After setValue:forKeyPath: %#", dict);
setValueForPathComponentsOfDictionary(#0, #[#"b",#"a"], dict);
NSLog(#"After setValueForPathComponentsOfDictionary %#", dict);
}
return 0;
}
Ok, I've found the solution.
- (BOOL)recurse:(NSDictionary*)dict keyToFound:(NSString*)ktf stack:(NSMutableArray*)stack parent:(NSString*)parent
{
BOOL result = NO;
for (NSString *key in [dict allKeys]) {
if ([key isEqualToString:ktf]) {
[stack insertObject:key atIndex:[stack count]];
return YES;
}
else {
if ([[dict valueForKey:key] isKindOfClass:[NSDictionary class]]) {
NSDictionary *d = [dict valueForKey:key];
[stack insertObject:key atIndex:[stack count]];
result = [self recurse:d keyToFound:ktf stack:stack parent:key];
if (!result) {
[stack removeLastObject];
}
}
}
}
return result;
}
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:#1,#"a",
#1,#"b",
#2,#"c",
[NSMutableDictionary dictionaryWithObjectsAndKeys:#4, #"e",
#5, #"f",
[NSMutableDictionary dictionaryWithObject:#6 forKey:#"h"], #"g", nil],#"d",
[NSMutableDictionary dictionaryWithObject:#7 forKey:#"k"],#"i",nil];
NSLog(#"Before %#",dict);
NSString *keyToChange = #"e";
NSString *valueToChange = #"Your New Value";
[dict enumerateKeysAndObjectsUsingBlock:^(id key ,id obj, BOOL *stop){
if([key isEqualToString:keyToChange])
{
NSLog(#"Found : %#",keyToChange);
if(![obj isKindOfClass:[NSArray class]] || ![obj isKindOfClass:[NSDictionary class]] || ![obj isKindOfClass:[NSMutableDictionary class]])
{
[dict setValue:#"newval" forKey:keyToChange];
return;
}
}
if([obj isKindOfClass:[NSDictionary class]])
{
[obj enumerateKeysAndObjectsUsingBlock:^(id key ,id obj1, BOOL *stop){
if([key isEqualToString:keyToChange])
{
if(![obj isKindOfClass:[NSArray class]] || ![obj isKindOfClass:[NSDictionary class]] || ![obj isKindOfClass:[NSMutableDictionary class]])
{
NSLog(#"Found : %#",keyToChange);
[obj setValue:#"newval" forKey:keyToChange];
return;
}
}
}];
}
}];
NSLog(#"After %#",dict);

Replace all NSNull objects in an NSDictionary

I'm curious, I currently have an NSDictionary where some values are set to an NSNull object thanks to the help of json-framework.
The aim is to strip all NSNull values and replace it with an empty string.
I'm sure someone has done this somewhere? No doubt it is probably a four liner and is simple, I am just far too burnt out to figure this out on my own.
I've made a few changes to Jacob's original answer to extend it to handle dictionaries and arrays stored within the original dictionary.
#import "NSDictionary+NullReplacement.h"
#import "NSArray+NullReplacement.h"
#implementation NSDictionary (NullReplacement)
- (NSDictionary *)dictionaryByReplacingNullsWithBlanks {
const NSMutableDictionary *replaced = [self mutableCopy];
const id nul = [NSNull null];
const NSString *blank = #"";
for (NSString *key in self) {
id object = [self objectForKey:key];
if (object == nul) [replaced setObject:blank forKey:key];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced setObject:[object dictionaryByReplacingNullsWithBlanks] forKey:key];
else if ([object isKindOfClass:[NSArray class]]) [replaced setObject:[object arrayByReplacingNullsWithBlanks] forKey:key];
}
return [NSDictionary dictionaryWithDictionary:[replaced copy]];
}
#end
And there's also an array category of course:
#import "NSArray+NullReplacement.h"
#import "NSDictionary+NullReplacement.h"
#implementation NSArray (NullReplacement)
- (NSArray *)arrayByReplacingNullsWithBlanks {
NSMutableArray *replaced = [self mutableCopy];
const id nul = [NSNull null];
const NSString *blank = #"";
for (int idx = 0; idx < [replaced count]; idx++) {
id object = [replaced objectAtIndex:idx];
if (object == nul) [replaced replaceObjectAtIndex:idx withObject:blank];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced replaceObjectAtIndex:idx withObject:[object dictionaryByReplacingNullsWithBlanks]];
else if ([object isKindOfClass:[NSArray class]]) [replaced replaceObjectAtIndex:idx withObject:[object arrayByReplacingNullsWithBlanks]];
}
return [replaced copy];
}
#end
With this, you can take any array or dictionary and recursively wipe out all the [NSNull null] instances.
P.S. For completion's sake, here are the header files:
#interface NSDictionary (NullReplacement)
- (NSDictionary *)dictionaryByReplacingNullsWithBlanks;
#end
And the array header:
#interface NSArray (NullReplacement)
- (NSArray *)arrayByReplacingNullsWithBlanks;
#end
Really simple:
#interface NSDictionary (JRAdditions)
- (NSDictionary *)dictionaryByReplacingNullsWithStrings;
#end
#implementation NSDictionary (JRAdditions)
- (NSDictionary *)dictionaryByReplacingNullsWithStrings {
const NSMutableDictionary *replaced = [self mutableCopy];
const id nul = [NSNull null];
const NSString *blank = #"";
for(NSString *key in self) {
const id object = [self objectForKey:key];
if(object == nul) {
//pointer comparison is way faster than -isKindOfClass:
//since [NSNull null] is a singleton, they'll all point to the same
//location in memory.
[replaced setObject:blank
forKey:key];
}
}
return [replaced copy];
}
#end
Usage:
NSDictionary *someDictThatHasNulls = ...;
NSDictionary *replacedDict = [someDictThatHasNulls dictionaryByReplacingNullsWithStrings];
Rolling through the dictionary hunting for NSNull is one way to tackle the problem, but I took a slightly lazier approach. Instead of nil you could assign an empty string, but the principle is the same.
CPJSONDictionary.h
#interface NSDictionary (CPJSONDictionary)
- (id)jsonObjectForKey:(id)aKey;
#end
CPJSONDictionary.m
#implementation NSDictionary (CPJSONDictionary)
- (id)jsonObjectForKey:(id)aKey {
id object = [self objectForKey:aKey];
if ([object isKindOfClass:[NSNull class]]) {
object = nil;
}
return object;
}
#end
I have tested Stakenborg solution. It works well, but it has following problem. If some object is expected to be number, for instance, converting it to NSNull can be a source of error.
I have create a new method to directly remove the NSNull entries. This way you only have to check that correspondant key exists.
Add in NSDictionary+NullReplacement
- (NSDictionary *)dictionaryByRemovingNulls{
const NSMutableDictionary *replaced = [self mutableCopy];
const id nul = [NSNull null];
for (NSString *key in self) {
id object = [self objectForKey:key];
if (object == nul) [replaced removeObjectForKey:key];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced setObject:[object dictionaryByRemovingNulls] forKey:key];
else if ([object isKindOfClass:[NSArray class]]) [replaced setObject:[object arrayByRemovingNulls] forKey:key];
}
return [NSDictionary dictionaryWithDictionary:[replaced copy]];
}
And in NSArray+NullReplacement
- (NSArray *)arrayByRemovingNulls {
NSMutableArray *replaced = [self mutableCopy];
const id nul = [NSNull null];
for (int idx = [replaced count]-1; idx >=0; idx--) {
id object = [replaced objectAtIndex:idx];
if (object == nul) [replaced removeObjectAtIndex:idx];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced replaceObjectAtIndex:idx withObject:[object dictionaryByRemovingNulls]];
else if ([object isKindOfClass:[NSArray class]]) [replaced replaceObjectAtIndex:idx withObject:[object arrayByRemovingNulls]];
}
return [replaced copy];
}
another variation:
NSDictionary * NewDictionaryReplacingNSNullWithEmptyNSString(NSDictionary * dict) {
NSMutableDictionary * const m = [dict mutableCopy];
NSString * const empty = #"";
id const nul = [NSNull null];
NSArray * const keys = [m allKeys];
for (NSUInteger idx = 0, count = [keys count]; idx < count; ++idx) {
id const key = [keys objectAtIndex:idx];
id const obj = [m objectForKey:key];
if (nul == obj) {
[m setObject:empty forKey:key];
}
}
NSDictionary * result = [m copy];
[m release];
return result;
}
The result is the same as, and it appears pretty much identical to Jacob's, but the speed and memory requirements are one half to one third (ARC or MRC) in the tests I made. Of course, you could also use it as a category method as well.
Here is my solution:
+ (NSDictionary *)cleanNullInJsonDic:(NSDictionary *)dic
{
if (!dic || (id)dic == [NSNull null])
{
return dic;
}
NSMutableDictionary *mulDic = [[NSMutableDictionary alloc] init];
for (NSString *key in [dic allKeys])
{
NSObject *obj = dic[key];
if (!obj || obj == [NSNull null])
{
// [mulDic setObject:[#"" JSONValue] forKey:key];
}else if ([obj isKindOfClass:[NSDictionary class]])
{
[mulDic setObject:[self cleanNullInJsonDic:(NSDictionary *)obj] forKey:key];
}else if ([obj isKindOfClass:[NSArray class]])
{
NSArray *array = [BasicObject cleanNullInJsonArray:(NSArray *)obj];
[mulDic setObject:array forKey:key];
}else
{
[mulDic setObject:obj forKey:key];
}
}
return mulDic;
}
+ (NSArray *)cleanNullInJsonArray:(NSArray *)array
{
if (!array || (id)array == [NSNull null])
{
return array;
}
NSMutableArray *mulArray = [[NSMutableArray alloc] init];
for (NSObject *obj in array)
{
if (!obj || obj == [NSNull null])
{
// [mulArray addObject:[#"" JSONValue]];
}else if ([obj isKindOfClass:[NSDictionary class]])
{
NSDictionary *dic = [self cleanNullInJsonDic:(NSDictionary *)obj];
[mulArray addObject:dic];
}else if ([obj isKindOfClass:[NSArray class]])
{
NSArray *a = [BasicObject cleanNullInJsonArray:(NSArray *)obj];
[mulArray addObject:a];
}else
{
[mulArray addObject:obj];
}
}
return mulArray;
}
-(NSDictionary*)stripNulls:(NSDictionary*)dict{
NSMutableDictionary *returnDict = [NSMutableDictionary new];
NSArray *allKeys = [dict allKeys];
NSArray *allValues = [dict allValues];
for (int i=0; i<[allValues count]; i++) {
if([allValues objectAtIndex:i] == (NSString*)[NSNull null]){
[returnDict setValue:#"" forKey:[allKeys objectAtIndex:i]];
}
else
[returnDict setValue:[allValues objectAtIndex:i] forKey:[allKeys objectAtIndex:i]];
}
return returnDict;
}
A category on nsnull that returns nil seems to also sense, at least to me. There are a few out there. One makes all calls return nil which seems to make sense. Sorry no link. I guess if you need to later use nspropertylistserialization the category might not work for you.

Problem with NSMutableArray and custom NSObject

I'm really desperate on this one. I'm trying to make a Framework which you can search and play YouTube videos with. But while testing it, I'm running in to a big problem.
In the search operation I'm adding YTVideos (a subclass of NSObject) to a NSMutableArray. When I loop thru it in the main(), I'm getting nil-objects:
Method
- (NSArray *)videosInRange:(NSRange)range {
if(range.length > 50) {
[NSException raise:#"Range lenth > 50"
format:#"The range of -videosInRange: can't be bigger than 50"];
return nil;
}
if((range.location + range.length) > 999) {
[NSException raise:#"Range to big"
format:#"The given range was to big (%d, %d)", range.location, range.length];
return nil;
}
NSString *searchURLString = [[self feedURL] absoluteString];
searchURLString = [searchURLString stringBySettingURLAttribute:#"start-index" value:[NSString stringWithFormat:#"%d",range.location + 1]];
searchURLString = [searchURLString stringBySettingURLAttribute:#"max-results" value:[NSString stringWithFormat:#"%d",range.length]];
NSLog(#"%#",searchURLString);
NSURL *url = [NSURL URLWithString:searchURLString];
NSXMLDocument *xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:url
options:0
error:NULL];
if(!xmlDoc)
return nil;
NSArray *videoElements = [[xmlDoc rootElement] elementsForName:#"entry"];
NSMutableArray *videos = [[NSMutableArray alloc] initWithCapacity:[videoElements count]];
register int i;
for(i = 0; i < [videoElements count]; i++) {
NSAutoreleasePool *addPool = [[NSAutoreleasePool alloc] init];
YTVideo *vid = [[YTVideo alloc] initWithXMLElement:[videoElements objectAtIndex:i]];
[videos addObject:vid];
[vid release];
[addPool drain];
}
NSArray *retValue = [NSArray arrayWithArray:videos];
[videos release];
return retValue;
}
main()
int main(int argc, const char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
YTSearchFeed *feed = [[YTSearchFeed alloc] initWithSearch:#"Eminem"];
long long results = [feed videoCount];
NSLog(#"%lld videos for search", results);
long long i = 0;
while(results != 0) {
int length = (results >= 50) ? (50) : (results);
NSArray *videos = [feed videosInRange:NSMakeRange(i, length)];
NSLog(#"L: %d", [videos count]);
int z;
for(z = 0; z < [videos count]; z++, i++) {
YTVideo *vid = [videos objectAtIndex:z];
NSString *title = [vid title];
NSLog(#"%d: %#", i+1, title);
}
results -= length;
}
[pool drain];
return NSApplicationMain(argc, argv);
}
I hope someone can take the time to look at this, and if you need anymore information, just ask.
Thank you in advance,
ief2
EDIT: YTVideo
- (id)initWithXMLElement:(NSXMLElement *)element {
self = [super init];
if(self != nil) {
_XMLElement = [element copy];
}
return self;
}
- (NSString *)title {
if(!_title) {
NSString *str = [[[self XMLElement] firstElementWithName:#"title"] stringValue];
_title = [[str stringByDecodingHTMLEntities] retain];
}
return [[_title copy] autorelease];
}
I get the title (and other video information) only when it's requested. the -stringByDecodingHTMLEntities works fine (Category on NSString).
I've rewritten the code and initialized all instance variables in the -initmethod