iOS - Best way to find object inverse - objective-c

I'm writing a recursive method that basically traverses a NSManagedObject object and converts it to a JSON dictionary. I have the bulk of this done, but I am running into an issue where the method goes into an infinite loop when it comes to an object's inverse.
For example, let's say the method starts off with an object that has a class of Job, and inside of that Job object there is a property called surveys. The surveys is an NSSet that contains multiple JobSurvey objects. Each JobSurvey object contains an inverse back to the original Job object and the property is called "Job".
When I run this through my method it starts the infinite loop by going into the job and starts to process each property. Once the method gets to the surveys property, it'll be called again to process each JobSurvey object as expected. The method then processes each property until it reaches the Job (inverse) object. At that time it'll continue to process that object, and thus creates the infinite loop.
Any thoughts on how I can fix this? I'm trying to write this method without having to create custom object classes with object mapping as it needs to be able to be used with any type of object I pass into it. Below is the code I have thus far.
- (NSDictionary *)encodeObjectsForJSON:(id)object
{
NSMutableDictionary *returnDictionary = [[NSMutableDictionary alloc] init];
// get the property list for the object
NSDictionary *props = [VS_PropertyUtilities classPropsFor:[object class]];
for (NSString *key in props) {
// get the value for the property from the object
id value = [object valueForKey:key];
// if the value is just null, then set a NSNull object
if (!value) {
NSNull *nullObj = [[NSNull alloc] init];
[returnDictionary setObject:nullObj forKey:key];
// if the value is an array or set, then iterate through the array or set and call this method again to encode it's values
} else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSSet class]]) {
NSMutableArray *retDicts = [[NSMutableArray alloc] init];
// encode each member of the array to a JSON dictionary
for (id val in value) {
[retDicts addObject:[self encodeObjectsForJSON:val]];
}
// add to the return dictionary
[returnDictionary setObject:retDicts forKey:key];
// else if this is a foundation object, then set it to the dictionary
} else if ([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]] || [value isKindOfClass:[NSNull class]] || [value isKindOfClass:[NSDate class]]) {
[returnDictionary setObject:value forKey:key];
// else this must be a custom object, so call this method again with the value to try and encode it
} else {
NSDictionary *retDict = [self encodeObjectsForJSON:value ];
[returnDictionary setObject:retDict forKey:key];
}
}
return returnDictionary;
}

This is what I did. It is similar to what you are trying to achieve.
link to da codes
It is a bit more explicit than i think you are looking for but I thought I'd suggest.

Related

valueForKeyPath failing with nested NSMutableDictionary objects

On my class I have two data objects: 'dataDict' (property) and 'prevDict' (instance variable) which are both NSMutableDictionary objects. In these multi-level dictionaries all the 'nodes' are themselves NSMutableDictionary objects and all the 'leaves' are NSString values.
Most of the time, these dictionaries will compare to be the same. But, when the new incoming data is different, I want to capture those changes via KVO and then save the new incoming dictionary. The recursive calls are the thing that fires off all the KVO changes.
Below is the code for recursively determining if the new incoming data (in property 'dataDict') is different than what's in the 'prevDictionary'. The initial call is:
[self postChangesFromPrevDict:prevDict usingKeyPath:#"dataDict"];
prevDict = [[NSMutableDictionary alloc] initWithDictionary:self.dataDict copyItems:YES];
If there's a change on a given leaf, it should update that in the dictionary and produce a KVO notification.
A sample 'newPath' string value could be "dataDict.group.name.points" -- in which the 'key' value is "points".
In the debugger breakpoint noted, I can see the values of 'curStr' and 'newStr' as, say, "120" and "121" with the these two values being correctly obtained from prevDict and dataDict, respectively.
If I obtain a value from a given keyPath then set it again using that SAME key path, why am I getting an NSUnknownKeyException?
I do know that all my dictionaries involved are mutable.
-(void) postChangesFromPrevDict:(NSMutableDictionary *)prevDictionary
usingKeyPath:(NSString *)keyPath
{
NSArray *keys = [prevDictionary allKeys];
for (NSString *key in keys) {
id obj = [prevDictionary objectForKey:key];
NSString *newPath = [keyPath stringByAppendingString:[NSString stringWithFormat:#".%#",key]];
if ([obj isKindOfClass:[NSDictionary class]])
[self postChangesFromPrevDict:obj usingKeyPath:newPath];
else {
NSString *curStr = obj;
NSString *newStr = [self valueForKeyPath:newPath];
//debug breakpoint
if (![newStr isEqualToString:curStr]) {
#try {
[self setValue:newStr forKeyPath:newPath];
}
#catch (NSException *__unused exception) {
NSLog(#"Can't set key on %#",newPath);
}
}
}
}
}
PS: To recurse, I test for an NSDictionary class which is fine as NSMutableDictionary is a subclass thereof.

What its __NSDictionaryM. Why NSStringFromClass don't return NSMutableDictionary?

I'm getting something weird that I can't get. I'm using a thirdy-party control (https://github.com/brunow/TableKit.m) and it do:
+ (NSSet *)cellMappingsForObject:(id)object mappings:(NSDictionary *)mappings {
NSString *objectStringName = NSStringFromClass([object class]);
return [mappings objectForKey:objectStringName];
}
It's create a mapping based in the name of the class:
[TKCellMapping mappingForObjectClass:[NSMutableDictionary class] block:^(TKCellMapping *cellMapping) {
//CODE
}];
And it check the datasource objects against it mappings. The objects are build for me as:
while ([rs next]) {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for (NSString *fieldName in [props allKeys]) {
//CODE
[dict setValue:fieldValue forKey:fieldName];
}
[list addObject :dict]; //<--This is the datasource. The map is against each dict
}
So, everywhere I'm telling it is NSMutableDictionary. But then this weird thing happend.
When it calls NSStringFromClass([NSMutableDictionary class]) it say NSMutableDictionary but when it calls NSStringFromClass([rowDict class]) it say __NSDictionaryM!
Why happend this? I check for the header of __NSDictionaryM and Xcode navigate to NSMutableDictionary (?). I can't create objects from __NSDictionaryM.
BTW, what I need to do for replace NSStringFromClass to get exactly NSMutableDictionary?
It's a private subclass of NSMutableDictionary. It is an NSMutableDictionary. Read more about class clusters here.
i've done it with a subclass check:
if( [[o class] isSubclassOfClass:[NSMutableDictionary class]] ) {
// o is at least a subclass of NSMutableDictionary
}

check id isKindOfType CFType

Shortly: how to determine if id is CFType or not at runtime
i'm implementing dynamic core data attributes and in willSave method of ExtendedManagedObject i wanna check if the id value is CFType to store it into plist file.
If I'm trying to save to plist UIImage, that is not toll-free bridged with CF (apple docs), I am getting an error:
2011-11-17 17:16:25.294 [490:707] Error saving extended data: Property list invalid for format (property lists cannot contain objects of type 'CFType')
Can I check it with some method or I have to implement by myself (just isKindOfClass from docs)?
I don't want to implement accessors in NSManagedObject subclass, I dont know exactly how many urls I'll get from entities properties. Question is about dynamic extended attributes at runtime.
- (void)willSave
{
NSDictionary *changes = [self valueForKey:#"extendedChanges"];
if (changes!=nil) {
// merge changes into snapshot
NSMutableDictionary *dict = [[self extendedSnapshot] mutableCopy];
NSEnumerator *e = [changes keyEnumerator];
NSString *key;
while (key=[e nextObject]) {
id value = [changes objectForKey:key];
if (value==[NSNull null])
[dict removeObjectForKey:key];
else if (#ugly and I'm not shure is thread safe **else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]] || [value isKindOfClass:[NSDate class]] || [value isKindOfClass:[NSData class]] || [value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]])**)
[dict setObject:value forKey:key];
}
// archive as binary plist
NSData *data = nil;
if ([dict count]>0) {
NSString *error=nil;
data = [NSPropertyListSerialization dataFromPropertyList:dict
format:NSPropertyListBinaryFormat_v1_0 errorDescription:&error];
if (error!=nil) {
NSLog(#"Error saving extended data: %#", error);
[error release];
}
}
[dict release];
[self setPrimitiveValue:data forKey:#"extendedData"];
}
[super willSave];
}
That's a wrong way to approach the problem. The blog post you referenced saves the extended attributes as a serialized plist. The plist can contain only the following types of objects, as written in the official doc:
NSArray, NSDictionary, NSDate, NSData, NSString and NSNumber.
Other classes are just not allowed. Don't add objects of any other class to the attributes extended this way.
Also, saving an image file in a CoreData database is not a good idea, generally speaking, read On Blobs in the official doc. Instead, save the UIImage in a file and write the file path in the CoreData database.
If you just have a transient property which is not saved to the database, you don't even have to go through the trouble of creating extended attributes. Just add it as a property of a subclass of NSManagedObject:
#interface MyManagedObject: NSManagedObject {
}
#property(retain, nonatomic) UIImage* thumbnail;
#property(retain, nonatomic) NSDictionary* thumbnailDictionary;
#end
without adding thumbnail in the CoreData model. Then do the corresponding #synthesize in the .m file. The property added this way to a managed object is just not saved.
If you want to keep unknown number of thumbnails, you can put an NSDictionary (or NSArray) containing UIImage*s.
If you really do want to get CFType from an NSObject*, do the following:
NSObject* x= ...;
CFTypeID typeID=CFGetTypeID((CFTypeRef)x);
if(typeID != CFStringGetTypeID() &&
typeID != CFDateGetTypeID() &&
typeID != CFDataGetTypeID() &&
typeID != CFDictionaryGetTypeID() &&
typeID != CFArrayGetTypeID() ) {
... it's a non-plistable type ...
}
The ugly if clause is there because there's no public function which gives you the CFTypeID of an un-bridged Objective-C object.
Read CFType reference. But I don't recommend doing this.
Trying to store an image in UIImage format also results in "property lists cannot contain objects of type CFType error. Best way is to convert UIImage to NSData before writing to pList.

Getting an object from an NSSet

If you can't get an object with objectAtIndex: from an NSSet then how do you retrieve objects?
There are several use cases for a set. You could enumerate through (e.g. with enumerateObjectsUsingBlock or NSFastEnumeration), call containsObject to test for membership, use anyObject to get a member (not random), or convert it to an array (in no particular order) with allObjects.
A set is appropriate when you don't want duplicates, don't care about order, and want fast membership testing.
NSSet doesn't have a method objectAtIndex:
Try calling allObjects which returns an NSArray of all the objects.
it is possible to use filteredSetUsingPredicate if you have some kind of unique identifier to select the object you need.
First create the predicate (assuming your unique id in the object is called "identifier" and it is an NSString):
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:#"identifier == %#", identifier];
And then choose the object using the predicate:
NSObject *myChosenObject = [mySet filteredSetUsingPredicate:myPredicate].anyObject;
NSArray *myArray = [myNSSet allObjects];
MyObject *object = [myArray objectAtIndex:(NSUInteger *)]
replace NSUInteger with the index of your desired object.
For Swift3 & iOS10 :
//your current set
let mySet : NSSet
//targetted index
let index : Int
//get object in set at index
let object = mySet.allObjects[index]
NSSet uses the method isEqual: (which the objects you put into that set must override, in addition, the hash method) to determine if an object is inside of it.
So, for example if you have a data model that defines its uniqueness by an id value (say the property is:
#property NSUInteger objectID;
then you'd implement isEqual: as
- (BOOL)isEqual:(id)object
{
return (self.objectID == [object objectID]);
}
and you could implement hash:
- (NSUInteger)hash
{
return self.objectID; // to be honest, I just do what Apple tells me to here
// because I've forgotten how Sets are implemented under the hood
}
Then, you can get an object with that ID (as well as check for whether it's in the NSSet) with:
MyObject *testObject = [[MyObject alloc] init];
testObject.objectID = 5; // for example.
// I presume your object has more properties which you don't need to set here
// because it's objectID that defines uniqueness (see isEqual: above)
MyObject *existingObject = [mySet member: testObject];
// now you've either got it or existingObject is nil
But yeah, the only way to get something out of a NSSet is by considering that which defines its uniqueness in the first place.
I haven't tested what's faster, but I avoid using enumeration because that might be linear whereas using the member: method would be much faster. That's one of the reasons to prefer the use of NSSet instead of NSArray.
for (id currentElement in mySet)
{
// ** some actions with currentElement
}
Most of the time you don't care about getting one particular object from a set. You care about testing to see if a set contains an object. That's what sets are good for. When you want to see if an object is in a collection sets are much faster than arrays.
If you don't care about which object you get, use -anyObject which just gives you one object from the set, like putting your hand in a bag and grabbing something.
Dog *aDog = [dogs anyObject]; // dogs is an NSSet of Dog objects
If you care about what object you get, use -member which gives you back the object, or nil if it's not in the set. You need to already have the object before you call it.
Dog *spot = [Dog dogWithName:#"Spot"];
// ...
Dog *aDog = [dogs member:spot]; // Returns the same object as above
Here's some code you can run in Xcode to understand more
NSString *one = #"One";
NSString *two = #"Two";
NSString *three = #"Three";
NSSet *set = [NSSet setWithObjects:one, two, three, nil];
// Can't use Objective-C literals to create a set.
// Incompatible pointer types initializing 'NSSet *' with an expression of type 'NSArray *'
// NSSet *set = #[one, two, three];
NSLog(#"Set: %#", set);
// Prints looking just like an array but is actually not in any order
//Set: {(
// One,
// Two,
// Three
// )}
// Get a random object
NSString *random = [set anyObject];
NSLog(#"Random: %#", random); // Random: One
// Iterate through objects. Again, although it prints in order, the order is a lie
for (NSString *aString in set) {
NSLog(#"A String: %#", aString);
}
// Get an array from the set
NSArray *array = [set allObjects];
NSLog(#"Array: %#", array);
// Check for an object
if ([set containsObject:two]) {
NSLog(#"Set contains two");
}
// Check whether a set contains an object and return that object if it does (nil if not)
NSString *aTwo = [set member:two];
if (aTwo) {
NSLog(#"Set contains: %#", aTwo);
}

Approaches to create a nested tree structure of NSDictionaries?

I'm parsing some input which produces a tree structure containing NSDictionary instances on the branches and NSString instance at the nodes.
After parsing, the whole structure should be immutable. I feel like I'm jumping through hoops to create the structure and then make sure it's immutable when it's returned from my method.
We can probably all relate to the input I'm parsing, since it's a query string from a URL. In a string like this:
a=foo&b=bar&a=zip
We expect a structure like this:
NSDictionary {
"a" => NSDictionary {
0 => "foo",
1 => "zip"
},
"b" => "bar"
}
I'm keeping it just two-dimensional in this example for brevity, though in the real-world we sometimes see var[key1][key2]=value&var[key1][key3]=value2 type structures. The code hasn't evolved that far just yet.
Currently I do this:
- (NSDictionary *)parseQuery:(NSString *)queryString {
NSMutableDictionary *params = [NSMutableDictionary dictionary];
NSArray *pairs = [queryString componentsSeparatedByString:#"&"];
for (NSString *pair in pairs) {
NSRange eqRange = [pair rangeOfString:#"="];
NSString *key;
id value;
// If the parameter is a key without a specified value
if (eqRange.location == NSNotFound) {
key = [pair stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
value = #"";
} else {
// Else determine both key and value
key = [[pair substringToIndex:eqRange.location] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
if ([pair length] > eqRange.location + 1) {
value = [[pair substringFromIndex:eqRange.location + 1] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
} else {
value = #"";
}
}
// Parameter already exists, it must be a dictionary
if (nil != [params objectForKey:key]) {
id existingValue = [params objectForKey:key];
if (![existingValue isKindOfClass:[NSDictionary class]]) {
value = [NSDictionary dictionaryWithObjectsAndKeys:existingValue, [NSNumber numberWithInt:0], value, [NSNumber numberWithInt:1], nil];
} else {
// FIXME: There must be a more elegant way to build a nested dictionary where the end result is immutable?
NSMutableDictionary *newValue = [NSMutableDictionary dictionaryWithDictionary:existingValue];
[newValue setObject:value forKey:[NSNumber numberWithInt:[newValue count]]];
value = [NSDictionary dictionaryWithDictionary:newValue];
}
}
[params setObject:value forKey:key];
}
return [NSDictionary dictionaryWithDictionary:params];
}
If you look at the bit where I've added FIXME it feels awfully clumsy, pulling out the existing dictionary, creating an immutable version of it, adding the new value, then creating an immutable dictionary from that to set back in place. Expensive and unnecessary?
I'm not sure if there are any Cocoa-specific design patterns I can follow here?
Expensive and unnecessary?
Yes. Apple's Cocoa APIs regularly say they return an immutable object, but actually return a mutable subclass that's been cast to the immutable version. This is a standard operating procedure and an accepted Cocoa design principle. You just trust that your clients aren't going to cast it back to a mutable version and change things from underneath you.
From Cocoa Core Competencies: Object Mutability:
Receiving Mutable Objects
When you call a method and receive an object in return, the object could be mutable even if the method’s return type characterizes it as immutable. There is nothing to prevent a class from declaring a method to return an immutable object but returning a mutable object in its implementation. Although you could use introspection to determine whether a received object is actually mutable or immutable, you shouldn’t. Always use the return type of an object to judge its mutability.
See also: Cocoa Fundamentals Guide: Cocoa Objects.