AFNetworking response object mapping - ios

I am trying out AFNetworking after coming from RestKit and I am wondering if there's a simpler solution than having to do this for all of my objects:
- (id)initWithAttributes:(NSDictionary *)attributes {
// ... init
_userID = [[attributes valueForKeyPath:#"id"] integerValue];
_username = [attributes valueForKeyPath:#"username"];
_avatarImageURLString = [attributes valueForKeyPath:#"avatar_image.url"];
return self;
}
This is how things appear in the AFNetworking basics example. But this looks like it might get cumbersome as soon as I'm returning objects with children. Is there an easier way for me to do this?
I saw that there is the AFIncrementalStore as referenced in this SO question. But I do not want any of the information retrieved to persist beyond the current session so I didn't think that was the right thing to do.
Thanks in advance for any help.

Related

Moving CoreData data blob into separate object

I am moving a NSData property out of a CoreData object and into a separate object, so self.pdfData becomes self.pdf.data. Does this look like the right approach to manage creation and deletion of the secondary object?
- (void)setPdfData:(NSData *)pdfData
{
if (!pdfData) {
if (self.pdf) {
[self.managedObjectContext deleteObject:self.pdf];
self.pdf = nil;
}
}
else {
if (!self.pdf) {
self.pdf = [BaseFormPDF insertInManagedObjectContext:self.managedObjectContext];
}
self.pdf.data = pdfData;
}
}
- (NSData *)pdfData
{
return self.pdf.data;
}
Yes, this is a good approach.
1) by moving the data to a separate entity you can fetch the main entity without loading the large data into memory.
2) psudo properties on managedObjects is are really cool and work very well for things like this. But I would be worried about doing too much in a setter. In this case I think it is OK, but doing more can cause issues. If a programmer is just setting thing.pdfData = data and lots of stuff is happening that the programmer didn't expect that could cause bugs.

NSMutableArray Interaction Troubles With Collisions

I am having trouble getting objects added to my NSMutableArray to log properly (which definitely means they won't process any of the appropriate functions correctly) with Spritebuilder [version 1.4.9, from the Apple App Store]. I am creating several objects using the same class, but each new one is overriding the older objects which exist. I thought an array would help keep things in order (and then on collision, I could call the array to check for which object was collided with), but it simply is not working that way - at all. Here is the relevant code.
Main.h
#property Coven *coven;
#property Nellie *nellie;
#property NSMutableArray *array;
//Physics, other things
Main.m
/Adding other things...
-(void) addCovenMember{
//This function is called on a RANDOM time interval
_array = [[NSMutableArray] alloc]init];
for (i = 0, i < 15, i++){
_coven = (Coven*) [CCBReader load:#"CovenMember"];
[_array addChild:_coven];
}
[_physicNode addChild:_coven];
}
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair nellie:(Nellie*)nellie coven:(Coven*)coven{
for (_coven in _array){
NSLog(#"%#",_coven.name)
if (CGRectIntersectsRect(_nellie.boundingBox, _coven.boundingBox){
NSLog(#"We're intersecting!");
}
}
Coven. h
//Nothing Important Here
Coven.m
-(void)didLoadFromCCB{
self.physicsBody.CollisionType = #"coven";
}
Nellie.h
//Nothing Here
Nellie.m
-(void) didLoadFromCCB{
self.physicsBody.CollisionType = #"nellie";
}
The collision is logging with every collision - but only as the name of the LATEST _coven member to be generated, no matter what I am colliding with. This also means that the _coven.boundingBox is solely on the latest _coven member and interaction only occurs when I hit the new member as soon as it generates on to the screen.
Any ideas? Any help?
Note: This is also posted on the Spritebuilder website - I decided to post it here as well because answers can be a little slow on those forums.
The -(void) addCovenMember overwrites (creates a new instance) of _array every time it's called. Thus, when you try to iterate in -ccPhysicsCollisionBegin: you'll only ever see 1 coven.
Add a nil check around your array creation:
if(_array == nil) {
_array = [[NSMutableArray] alloc]init];
}
The for loop in the -addCovenMember method looks broken (at least not a c loop). Reaplace the , with ;:
for (i = 0; i < 15 i++){
Also, using for(_coven in _array) seems wrong, you already have a property self.coven (presumably) with a backing _coven ivar. Try changing it to for(Coven * c in self.array) and use the local c in the loop:
for (Coven * c in _array){
NSLog(#"%#",c.name)
if (CGRectIntersectsRect(_nellie.boundingBox, c.boundingBox){
NSLog(#"We're intersecting!");
}
}
To everyone out in the world struggling with their ccPhysicsCollisions, arrays may not be the answer - this was a simple fix that left me incapacitated for days.
Using the basic ccPhysicsCollisionsBegan that ships with spritebuilder, try this without arrays first:
Scene.m
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair nellie:(Nellie*)nellie coven:(Coven*)coven{
[_coven stopAction:coven.path];
}
I initially created the method with:
[_coven stopAction:_coven.path];
Yes, that (underscore) set me back three weeks. Be sure you refer to the object interacting through the physics delegate, and not the object itself, which in my case, was constantly being overwritten by the new ones being generated.
Check your underscores.
Solved! :D
Thanks to #thelaws for your help! I'll get better at Obj C... eventually.

Faster way to pull specific data from NSMutableArray?

I have an array full of NSObjects I created called "Questions".
One property of each Question is which level it belongs to.
If the user has chosen to play level 2, I want to get all the Questions that have a .level property of 2. Right now I am looping through all the questions to find the matches, but this is taking ~2 seconds on an iPad 3 / new iPad device. Is there a faster way of dealing with a situation like this?
int goThrough = 0;
do {
Question *currentQuestion = [allQs objectAtIndex:(goThrough)];
if (currentQuestion.level == levelChosen) {
[questions addObject:currentQuestion];
}
goThrough++;
} while (goThrough < [allQs count]);
Your help is greatly appreciated!
If you have to organize the questions by level on a regular basis, then why not keep all of the questions organized by level. Create a dictionary of arrays. Each key if the level and each array is the list of questions for that level. You do this once and it becomes trivial to get the questions for a level.
I dont have access to a mac at the moment but you can give a try to this:
[allQs enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger index, BOOL *stop) {
Question *currentQuestion = [allQs objectAtIndex:index];
if (currentQuestion.level == levelChosen) {
[questions addObject:currentQuestion];
}
}
This will use all the cores of your device so it can be twice as fast
You could always use fast enumeration (which, unless you intend on mutating the objects is the fastest way to enumerate a collection). Something like this:
for (Question *thisQuestion in allQs) {
if (thisQuestion.level == levelChosen)
[questions addObject:thisQuestion];
}
}
Since you are not mutating the collection you are iterating through (allQs), this would work fine and be faster than using enumerateObjectsUsingBlock. If you need the index of the array you are iterating through (allQs), then use enumerateObjectsUsingBlock.
I would suggest using the NSArray method enumerateObjectsUsingBlock or one of it's variants. There are even variants that will loop through the array elements concurrently. You'd probably need to use a lock to add elements to your questions array however, since I doubt if NSMutableArray's addObject method is thread-safe.
You should probably test a non-concurrent version against a concurrent version with locking to see which is faster. Which approach is faster would depend on how many of the objects in the allQs array belong to the current level. If only a few belong, the code that asserts a lock won't fire very often, and the benefit of concurrency will outweigh the time penalty of asserting a lock. If most of the objects in the allQs array match the chosen level, code will end up spending a lot of time asserting locks, and the concurrent threads will still waiting for other threads to release a lock.
Modified code might look something like this:
single-threaded version:
[allQs enumerateObjectsUsingBlock:
^(Question *currentQuestion, NSUInteger index, BOOL *stop)
{
if (currentQuestion.level == levelChosen)
[questions addObject:currentQuestion];
}
];
Concurrent version:
[allQs enumerateObjectsWithOptions:
NSEnumerationConcurrent
usingBlock:
^(Question *currentQuestion, NSUInteger index, BOOL *stop)
{
if (currentQuestion.level == levelChosen)
#synchronized
{
[questions addObject:currentQuestion];
}
}
];
Actually, now that I think about it, you would likely get still faster performance by first doing a concurrent pass on the array using indexesOfObjectsWithOptions:passingTest. In that pass you'd build an NSIndexSet of all the objects that match the current level. Then in one pass you'd extract those elements into another array:
NSIndexSet *questionIndexes = [allQs indexesOfObjectsWithOptions: NSEnumerationConcurrent
usingBlock:
^(Question *currentQuestion, NSUInteger index, BOOL *stop)
{
return (currentQuestion.level == levelChosen)
}
];
questions = [allQs objectsAtIndexes: questionIndexes];
Another poster pointed out that you are better off breaking up your arrays of questions up by level in advance. If that works with your program flow it's better, since not filtering your array at all will always be faster than the most highly optimized filtering code.
There is a simple answer that seems to be missing. If you want to filter the objects of an array to only have certain ones left, -filteredArrayUsingPredicate: is what you would want. It can be done exceptionally simply.
NSPredicate *p = [NSPredicate predicateWithBlock:^(Question *aQuestion, NSDictionary *bindings){
return (aQuestion.level==2);
}];
NSArray *filteredArray = [originalArray filteredArrayUsingPredicate:p];

Why can't I add to a dictionary within my model from my controller? [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
My model is intended to store JSON data. Part of what's to be stored is an array nested in the JSON, which could be a varying number of elements. This is how I retrieve that data in code.
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"SomeJsonFile" ofType:#"json"];
NSData *jsonFromFile = [[NSData alloc] initWithContentsOfFile:filePath];
NSError *error = nil;
NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:jsonFromFile options:0 error:&error];
DLog(#"simulated 'Stuff' JSON, stored in dictionary: %#", jsonData);
That works great. A callback sends along that dictionary, at which point I experience my problem. Here's an example of the nested array in the JSON.
stuff = (
{
id = 11;
"someState" = 1;
someNumber = 100;
description = "Human-readable text for thing #11!";
},
{
id = 22;
"someState" = 0;
someNumber = 20;
description = "Human-readable text for thing #22!";
},
{
id = 33;
"someState" = 1;
someNumber = 250;
description = "Human-readable text for thing #33!";
},
);
My model should store the data sent along there, but since that nested array is an unknown amount of elements, I've elected to:
Use an NSMutableArray to store the IDs of each array element.
Use an NSMutableDictionary for each property of the element (like someState, someNumber, description).
When the time comes to list all the elements of the model, I'll iterate through the array of IDs and use each ID as a key for each of the NSMutableDictionary properties, thus retrieving everything I need.
Those NSMutableDictionary properties from step 2 aren't responding. They don't set the keys or values; they remain null. From other questions, it seems this is because they aren't initialized. Also from other questions, I've been able to add and remove keys/values to an arbitrary NSMutableDictionary that I initialized (not a part of the model).
The controller should be blind to this initialization pertinent to the model, but I can't seem to initialize the NSMutableDictionary properties by overriding the getters/setters of the model. What is the correct way in objective-C to set up my model with NSMutableDictionary properties such that I can set keys and values in it from my controller?
I also feel like my many-dictionaries indexed by ids in an array solution is overkill. If you can spot that and know the better way to deal with this in iOS, please feel free to lend your wisdom.
EDIT
At the request of Marcus Adams, here is the code where I use the NSMutableDictionary properties. I do not initialize the NSMutableDictionary properties; where to do that so that it's appropriate to the MVC model is my question. His answer has led me to try initializing by overriding "init", and this works as part of the instantiation of the model (Marcus Adams will be marked as the answer if he provides the explanation, since he led me to the answer, but answers with code samples for correctly overriding "init" will be up voted).
// Now we're ready to store what's in the JSON.
NSDictionary *stuff = jsonData[#"stuff"];
NSMutableDictionary *tempDictBecauseAllocNeeded = [[NSMutableDictionary alloc] init];
for (NSDictionary *thing in stuff) {
[tempDictBecauseAllocNeeded setObject:thing[#"description"] forKey:thing[#"id"]]; // This works!
theModel.thingDescriptions[thing[#"id"]] = thing[#"description"]; // This wasn't working!
[theModel.thingIds addObject:thing[#"id"]]; // This is the array of ids used to retrieve values from each dictionary
}
I guess your code should be working now. I was curious how you were copying the NSDictionaries and thought the problem might be there. Obviously, you discovered that you hadn't initialized tempDictBecauseAllocNeeded. I wouldn't use a getter to initialize unless you are keeping tempDictBecauseAllocNeeded around for awhile. If you are, storing it as a property and initializing it in a getter is the simplest thing to do.
// Getter ensures that when you reference self.tempDictBecauseAllocNeeded
/// it's always initialized
-(NSMutableDictionary *) tempDictBecauseAllocNeeded {
if (!_tempDictBecauseAllocNeeded) {
_tempDictBecauseAllocNeeded = [[NSMutableDictionary alloc] init];
}
return _tempDictBecauseAllocNeeded;
}
Since the original JSON parsing, by default, creates mutable leaves, even though you assign it to an NSDictionary, each leaf in there is still mutable.
So, when you (shallow copy) over the leaves to your NSMutableDictionary, they are still mutable, of course.
stuff = ( ...
This is not a JSON array, is it supposed to be? JSON array would be
stuff = [ ...

Timing loop results

I got a little stuck and I'm hoping someone can point me in the right direction. I have an NSMutableArray that stores a sequence. I created an enumerator so that a while loop can get the content of the array one by one.
Everything works fine however I want the methods to be called with a 10 second gap in between each call. Right now it plays all at once (or in very quick order). What should I look at to create a delay in between method calls?
Below is what I got so far. Thanks!
NSEnumerator * enumerator = [gameSequenceArray objectEnumerator];
id element;
while(element = [enumerator nextObject])
{
NSLog(element);
int elementInt = [element intValue];
[self.view showButton:elementInt];
}
You almost certainly don't want to stick a "delay" in your loop, which will block the thread until it's over (and, unless your app is multi-threaded, block the entire thing). You could use multiple threads, but instead I'd probably split the loop out over repeated timed calls of another selector. Store the enumerator (or current index), and then look at NSObject's performSelector:awithObject:afterDelay:
So something like
[NSObject performSelector:#selector(some:selector:name:) withObject:objInstance afterDelay: 10]
where the selector will pickup the current enumerator, use it, advance it and schedule another call. Make sure you don't allow changes to the collection whilst this set of timed methods is executing.
This is what NSTimer is for. Use NSTimer to get each element in the array sequentially.
As an aside: you might want to take a look at Objective-C 2.0's Fast Enumeration
if gameSequenceArray is an array, then you don't need to use an enumerator:
NSTimeInterval time = 10;
for (id elementId in gameSequenceArray) {
[self.view performSelector:#selector(showButton:) withObject:elementID afterDelay:time];
}
and then you declare showButton:
- (void)showButton:(id)anElement {
...
}
If you end up passing your object enumerator around with a timer, know that you are not allowed to modify your array's contents until you are finished enumerating it.
So here was the solution that I came up with based on everyones input.
NSEnumerator * enumerator = [gameSequenceArray objectEnumerator];
NSTimeInterval time = 5;
for (NSString *element in enumerator) {
id elementId = element;
time++;
[self.view performSelector:#selector(showButton:) withObject:elementId afterDelay:time];
}
Thanks again for pointing me in the right direction everyone.

Resources