So I am using restkit for asynchronous calls and such, and felt the one of the best ways to handle possible connection issues was to have a requestManager Which holds a single an array of requests and it will keep looping through it and remove whenever they are successful, this way the payload for the request can be created and added to the manager and the rest of the app can continue without having to worry about returns or not unless it is a GET... Now my issue is that some of the requests are using closures or code Blocks as you will.
And i am having trouble wrapping my head around the syntax and usage of it properly. Especially when it comes to blocks.
This is how I plan to implement it...
so I first Make this Call
GameInfo *game = [[GameInfo alloc] init];
game.myWTFID = [playerInfo _wtfID];
game.opponentWTFID = [gameInfoObj GetOpponentWTFID];
game.gameType = [NSNumber numberWithInt:[gameInfoObj gameType]];
game.turnNumber = [gameInfoObj GetTurnNumber];
game.lastMove = [gameInfoObj GetGameID];
[[RequestPayload sharedRequestPayload] addPutRequestForObject : game withSerializationMapping : serializedGameMap forClass : [Game class] withBlock :^(RKObjectLoader *loader)
{
[loader setObjectMapping:gameMap];
loader.delegate = self;
loader.onDidLoadResponse = ^(RKResponse *response)
{
[game release];
};
loader.targetObject = nil;
loader.backgroundPolicy = RKRequestBackgroundPolicyRequeue;
}
];
here Is the Implementation
- ( void ) addPutRequestForObject : (id) object withSerializationMapping : (RKObjectMapping*) serialMapping forClass : (Class) class withBlock : ( void(^)(RKObjectLoader*) ) block
{
NSMutableDictionary *dict = [NSMutableDictionary new];
NSNumber *postType = [[NSNumber alloc]initWithInt:1];
[dict setObject:postType forKey:#"request"];
[dict setObject:object forKey:#"data"];
[dict setObject:serialMapping forKey:#"serialMapping"];
[dict setObject:class forKey:#"class"];
void (^ copyBlockLoader)(id,RKObjectMapping*) = Block_copy(block);
[dict setObject:copyBlockLoader forKey:#"block"];
Block_release(copyBlockLoader);
[postType release];
postType = nil;
[_RequestsToInvoke addObject:dict];
}
and then within a for loop after going though each object in the array which will be a dictionary containing the needed information to do something like this.(apologies if this does not make sense it is highly contrived as the actual method is alot longer but the vital part i thought was here.)
[[RKObjectManager sharedManager].mappingProvider setSerializationMapping:[dict objectForKey:#"serialMapping"] forClass:[dict objectForKey:#"class"]];
void(^block)(RKObjectLoader *) = [dict objectForKey:#"block"];
[[RKObjectManager sharedManager] putObject:object usingBlock:block];
So my questions are
in the first snippet where i ask it to release the game object...Will that work? or will it result in a leak the game object is declared within the same scope as that call so the only pointer i have left is the one in the code block.
am i saving the block into the dictionary correctly?
and last does anyone have a better alternative? or spot something else with my implementation that needs correcting/modification?
Related
I just learned how to make use of KVO, but only the basics. What I need to achieve is something like this:
I have a delegate call that passes a Speaker object.
- (void)onSpeakerFound:(Speaker *)speaker
Once I receive this Speaker in the UI part, from there I will assign observers for this object.
But, this is just for one speaker. What if I have multiple speakers to keep track of. I need to assign observers separately for those speakers and then at the same time I wish to keep their references for further updates to the values.
Each speaker could be updated from time to time. So when I notice that there is a change that happened on a speaker, I wish to access the reference to that speaker and update the values just like how NSMutableDictionary works.
NSMutableDictionary makes a copy of an object set to it so it will be a difference object if I get it again from the dictionary.
So, is there a class that allows me to keep track of an object by just keeping a reference only to that object without making a copy of it?
EDIT: A Test Made To Verify That When An Instantiated Object is Set in an NSMutableDictionary, The Instantiated Object is not referenced with the one set inside NSMutableDictionary.
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
NSString *obj = #"initial value";
NSString *key = #"key";
[dict setObject:obj forKey:key];
NSLog(#"Object is now %#", [dict objectForKey:key]);
obj = #"changed value";
NSLog(#"Object is now %#", [dict objectForKey:key]);
}
Log:
2016-07-26 21:04:58.759 AutoLayoutTest[49723:2144268] Object is now initial value
2016-07-26 21:04:58.761 AutoLayoutTest[49723:2144268] Object is now initial value
NSMutableDictionary makes a copy of an object set to it...
That is not correct; it will add a reference to the object. It will be the same object referenced inside and outside the Objective-C collection.
So, is there a class that allows me to keep track of an object...?
Probably NSMutableSet if you just want a list of the objects. That will take care that you have a unique reference to each object, however you need to implement the methods hash and isEqual on those objects so they behave correctly. Otherwise NSMutableDictionary if you want fast look-up by key.
-try this one
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
NSString *obj = #"initial value";
NSString *key = #"key";
[dict setObject:obj forKey:key];
NSLog(#"Object is now %#", [dict objectForKey:key]);
obj = #"changed value";
[dict setObject:obj forKey:Key];
NSLog(#"Object is now %#", [dict objectForKey:key]);
}
I have a number of custom classes, subclassed from NSObject, that I'm trying to write to a JSON file. My top level class, called Survey, is a singleton, and one of its properties is addressArray, which contains instances of another class called Address.
There can be any number of addresses in Survey's addressArray, and I'm trying to write all the data to a JSON file. My code is as follows:
//In both Survey.m and Address.m, I have a method like this:
- (NSDictionary *)surveyDictionaryRepresentation
{
NSMutableDictionary *dictionary = [NSMutableDictionary new];
dictionary[#"name"] = [self name] ? : [NSNull null];
dictionary[#"emailAddress"] = [self emailAddress] ? : [NSNull null];
dictionary[#"addressArray"] = [self addressArray] ? : [NSNull null];
dictionary[#"storage"] = [NSNumber numberWithInteger:[self storage]] ? : [NSNull null];
dictionary[#"extraStops"] = [NSNumber numberWithBool:[self extraStops]] ? : [NSNull null];
return [NSDictionary dictionaryWithDictionary:dictionary];
}
Then, in the view controller, I have the following in the ViewDidLoad method.
NSArray *surveys = #[[[Survey sharedInstance] surveyDictionaryRepresentation]];
NSMutableArray *addresses = [[NSMutableArray alloc]init];
for (Address *address in [[Survey sharedInstance]addressArray]){
//What the hell was I thinking?
[addresses addObject:[addressaddressDictionaryRepresentation]];
}
NSDictionary *dictionary = #{#"Survey": surveys, #"Address":addresses};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dictionary options:NSJSONWritingPrettyPrinted error:nil];
NSLog(#"%#", [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]);
Now, I'm getting the following error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (Address)'
I can see why there would be a problem. Because addressArray, containing instances of Address, is in Survey, there seems to be a problem with adding the Address data to the JSON object created for Survey. I'm not sure how to get around this problem. After using breaking points to step through the program, I see that the crash is occurring after it attempts to execute the line: NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dictionary options:NSJSONWritingPrettyPrinted error:nil];
For what it's worth, I NSLog'd the addresses array after looping through it and running the addressesDictionaryRepresentation method on its contents, and it logged perfectly, showing all the properties and all the values perfectly. I just cant get it into the JSON document. Any suggestions? Help is very much appreciated.
EDIT: First few lines of the log of AddressArray:
2015-02-18 11:21:29.122 [70958:920733] (
{
aCCOI = "-1";
aCfloorNumber = "<null>";
activity = 0;
addressType = "-1";
allowedToReserveDock = "-1";
EDIT 2: The only other code in Survey.m is that which establishes the singleton:
+ (instancetype)sharedInstance {
static Survey *_instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[Survey alloc] init];
});
return _instance;
}
EDIT3:
if ([self addressArray]){
NSMutableArray *addresses = [[NSMutableArray alloc]init];
for (Address *address in [[Survey sharedInstance]addressArray]){
[addresses addObject:[address addressDictionaryRepresentation]];
}
dictionary[#"addressArray"] = [self addressArray];
} else {
dictionary[#"addressArray"] = [NSNull null];
}
[self addressArray] should return an JSON array, not one with Address objects. Just like you did for the #"Address" field:
NSMutableArray *addresses = [[NSMutableArray alloc]init];
for (Address *address in [[Survey sharedInstance]addressArray]){
[addresses addObject:[addressaddressDictionaryRepresentation]];
}
Return that NSMutableArray addresses so it gets set as value for #"addressArray". Then you should be fine.
What I tend to do, in the ClassX toDictionary method, is create a mutable dictionary, then insert each instance value/property as is appropriate. For a pointer to another class I call the toDictionary method of that class. For a pointer to an array of ClassY objects, I create a mutable array, then call the toDictionary method of ClassY for each element of the array.
There's no real point in creating a separate toArray method to handle an array of ClassY, since the array handling is easy "boilerplate" to code directly in the calling method.
I have this code, but when I log the mediaDictionaryArray, I get null. Does the receiver array have to be initialized with a value first or can I add objects to an empty array? Does [NSArray array] vs. [[NSArray alloc]init] have anything to do with it?
Adding dictionary from API call that happens i times. Asynch call will return the dictionary - can't be sure if NSMutableArray will work in catchJSONArray since asynch nature of call will make the array of indeterminate size which will make it hard to use later on.
Updated with relevant bit.
for (int i = 0; i<[array count]; i++) {
NSString *getString = array[i];
NSLog(#"getstring %#", getString);
[client GET:getString parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
if (httpResponse.statusCode == 200) {
dispatch_async(dispatch_get_main_queue(), ^{
_locationMediaArray = (NSArray*)responseObject[#"data"];
[self catchJSONArray:_locationMediaArray];
then here is method with the array issue
-(void)catchJSONArray:(NSArray*)array{
NSArray *catchJSONArray = [NSArray array];
_mediaDictionaryArray = [catchJSONArray arrayByAddingObjectsFromArray:array];
NSLog(#"mediaDictionaryArray %#", _mediaDictionaryArray);
}
arrayByAddingObjectsFromArray returns a new array containing your objects, as an NSArray can not be changed once created.
If you want to change an existing array, you should be using an NSMutableArray.
The best way you could do this is:
_mediaDictionaryArray=[NSArray arrayWithArray:otherArray];
That will create a new array with the contents of otherArray and assign it to _mediaDictionary.
No need to alloc init your array just pass the refrence of your other array. If
_mediaDictionaryArray is mutable array then use below:-
_mediaDictionaryArray=[array mutableCopy];
If it is non mutable array then use below
_mediaDictionaryArray=[array copy];
I am not figuring out how to perform a query in Azure. I did finally figure out inserts, but now I am trying to query from Azure. Two parts here, how do I return the result from Azure and how do I read the results in objective-C?
Thus far, I have this
-(double)GetValidAppVersion
{
// Create a proxy client for sending requests to the Azure platform.
MSClient *client = [MSClient clientWithApplicationURLString : #""
withApplicationKey : #"];
MSTable *appSettingsTable = [client getTable:#"AppSettings"];
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"Key == AppVersion"];
NSArray *queryResults = [[NSArray alloc] init];
[appSettingsTable readWhere:predicate completion:^(NSArray *results, NSInteger totalCount, NSError *error)
{
self.items = [results mutableCopy];
}];
return 1.0;
}
I have not figured out the Azure side either. How can I query and return a result based on the input parameter?
My table is simple with
ID int
Key varchar
Value varchar
Any help with getting this going is greatly appreciated.
Edit:
I added this to my controller
-(bool) IsAppVersionValid
{
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
double validAppVersion = [delegate.appVersion doubleValue];
double serverAppVersion;
NSDictionary *item = #{ #"complete" : #(NO) };
[self.Service SelectAppVersion:item completion:^(NSUInteger index)
{
}];
return true;//clientVersion >= validAppVersion;
}
And this to my service (this is sloppy as it should be a simple completion block -- I would like to pass NSString * with the AppSettings key value and use that in the predicate as well. Any thoughts on the syntax for that?
typedef void (^CompletionWithAppVersionBlock)(NSUInteger index);
- (void) SelectAppVersion:(NSDictionary *) item
completion:() completion;
All of the read table read methods that are part of the iOS SDK for Mobile Services are asynchronous which means that you have to pass a completion block into them (as you're doing above where you're setting self.items = [results mutableCopy];) in order to then do something with the results they are fetching.
This means that in order to get the value you're looking for, you'll want to pass in a completion block into your GetValidAppVersion method. You can then pass the app version you're getting back to that block. So something like this:
-(void) GetValidAppVersion:(NSDictionary *)item completion:(CompletionWithVersion)completion
{
MSTable *appSettingsTable = [client getTable:#"AppSettings"];
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"Key == AppVersion"];
NSArray *queryResults = [[NSArray alloc] init];
[appSettingsTable readWhere:predicate completion:^(NSArray *results, NSInteger totalCount, NSError *error)
{
completion([results objectAtIndex:0]);
}];
}
You would need to define the CompletionWithVersion as being a block with a parameter returned (the AppVersion). Take a look at the iOS quickstart application to see how the completion blocks are defined.
I'm almost there understanding simple reference counting / memory management in Objective-C, however I'm having a difficult time with the following code. I'm releasing mutableDict (commented in the code below) and it's causing detrimental behavior in my code. If I let the memory leak, it works as expected, but that's clearly not the answer here. ;-) Would any of you more experienced folks be kind enough to point me in the right direction as how I can re-write any of this method to better handle my memory footprint? Mainly with how I'm managing NSMutableDictionary *mutableDict, as that is the big culprit here. I'd like to understand the problem, and not just copy/paste code -- so some comments/feedback is ideal. Thanks all.
- (NSArray *)createArrayWithDictionaries:(NSString *)xmlDocument
withXPath:(NSString *)XPathStr {
NSError *theError = nil;
NSMutableArray *mutableArray = [[[NSMutableArray alloc] init] autorelease];
//NSMutableDictionary *mutableDict = [[NSMutableDictionary alloc] init];
CXMLDocument *theXMLDocument = [[[CXMLDocument alloc] initWithXMLString:xmlDocument options:0 error:&theError] retain];
NSArray *nodes = [theXMLDocument nodesForXPath:XPathStr error:&theError];
int i, j, cnt = [nodes count];
for(i=0; i < cnt; i++) {
CXMLElement *xmlElement = [nodes objectAtIndex:i];
if(nil != xmlElement) {
NSArray *attributes = [NSArray array];
attributes = [xmlElement attributes];
int attrCnt = [attributes count];
NSMutableDictionary *mutableDict = [[NSMutableDictionary alloc] init];
for(j = 0; j < attrCnt; j++) {
if([[[attributes objectAtIndex:j] name] isKindOfClass:[NSString class]])
[mutableDict setValue:[[attributes objectAtIndex:j] stringValue] forKey:[[attributes objectAtIndex:j] name]];
else
continue;
}
if(nil != mutableDict) {
[mutableArray addObject:mutableDict];
}
[mutableDict release]; // This is causing bad things to happen.
}
}
return (NSArray *)mutableArray;
}
Here's an equivalent rewrite of your code:
- (NSArray *)attributeDictionaries:(NSString *)xmlDocument withXPath:(NSString *)XPathStr {
NSError *theError = nil;
NSMutableArray *dictionaries = [NSMutableArray array];
CXMLDocument *theXMLDocument = [[CXMLDocument alloc] initWithXMLString:xmlDocument options:0 error:&theError];
NSArray *nodes = [theXMLDocument nodesForXPath:XPathStr error:&theError];
for (CXMLElement *xmlElement in nodes) {
NSArray *attributes = [xmlElement attributes];
NSMutableDictionary *attributeDictionary = [NSMutableDictionary dictionary];
for (CXMLNode *attribute in attributes) {
[attributeDictionary setObject:[attribute stringValue] forKey:[attribute name]];
}
[dictionaries addObject:attributeDictionary];
}
[theXMLDocument release];
return attributeDictionaries;
}
Notice I only did reference counting on theXMLDocument. That's because the arrays and dictionaries live beyond the scope of this method. The array and dictionary class methods create autoreleased instances of NSArray and NSMutableDictionary objects. If the caller doesn't explicitly retain them, they'll be automatically released on the next go-round of the application's event loop.
I also removed code that was never going to be executed. The CXMLNode name method says it returns a string, so that test will always be true.
If mutableDict is nil, you have bigger problems. It's better that it throws an exception than silently fail, so I did away with that test, too.
I also used the relatively new for enumeration syntax, which does away with your counter variables.
I renamed some variables and the method to be a little bit more Cocoa-ish. Cocoa is different from most languages in that it's generally considered incorrect to use a verb like "create" unless you specifically want to make the caller responsible for releasing whatever object you return.
You didn't do anything with theError. You should either check it and report the error, or else pass in nil if you're not going to check it. There's no sense in making the app build an error object you're not going to use.
I hope this helps get you pointed in the right direction.
Well, releasing mutableDict really shouldn't be causing any problems because the line above it (adding mutableDict to mutableArray) will retain it automatically. While I'm not sure what exactly is going wrong with your code (you didn't specify what "bad things" means), there's a few general things I would suggest:
Don't autorelease mutableArray right away. Let it be a regular alloc/init statement and autorelease it when you return it ("return [mutableArray autorelease];").
theXMLDocument is leaking, be sure to release that before returning. Also, you do not need to retain it like you are. alloc/init does the job by starting the object retain count at 1, retaining it again just ensures it leaks forever. Get rid of the retain and release it before returning and it won't leak.
Just a tip: be sure that you retain the return value of this method when using it elsewhere - the result has been autoreleased as isn't guaranteed to be around when you need it unless you explicitly retain/release it somewhere.
Otherwise, this code should work. If it still doesn't, one other thing I would try is maybe doing [mutableArray addObject:[mutableDict copy]] to ensure that mutableDict causes you no problems when it is released.
In Memory Management Programming Guide under the topic Returning Objects from Methods (scroll down a bit), there are a few simple examples on how to return objects from a method with the correct memory managment.