I am having this horrible crash out of my application that I have been trying to resolve for several days now. The code is an NSURLRequest to send a request to a web-service. I already use the web-service in production and I simply added this new service request. I finally added #try ... #catch around the offending code and it still crashes the application and does not "catch" anything.
How do I debug this?
I have used code inspection, including a peer review.
The code is like this ... where "httpClient" is an NSObject with:
id _delegateConnection;
id _delegateReceiver;
NSURLConnection *connection;
NSMutableURLRequest *urlServiceRequest;
NSMutableString *bodyRequest;
NSHTTPURLResponse *httpResponse;
NSMutableData *responseData;
NSError *error;
#try {
[httpClient setDelegateReceiver: self];
[httpClient buildRequest: dictParameters];
[httpClient executeRequest];
}
#catch (NSException *exception) {
NSLog(#"%s: %#", __FUNCTION__, exception);
}
#finally {
// Add code here ...
}
I found the problem and it was through good-fortune and code inspection in a different part of the code ("where the light was better" :)
As it turns out, I have a dictionary of parameters that get written to a JSON string, added to the HTTP body of the urlServiceRequest object listed in my sample code. What I finally stumbled upon was that the parameter container (NSObject, not NSDictionary) was not "retained", therefore it was probably "autoreleased", while still referenced within my buildRequest method, by the posting of the urlServiceRequest.
Poor memory management on my part led me astray in my troubleshooting.
Related
I am attempting to use AFNetworking to upload an audio file to Cloudinary through a POST call. I have been using AFNetworking to send POST calls to my own server with ease but this is the first time I have tried sending an NSData object through AFNetworking which is where I think the problem lies and am not exactly sure how I need to adjust the call to get it to work.
My Code
I need to send 4 parameters through the post call to the Cloudinary service which I place in to a dictionary....
NSData *audioFile = [[NSData alloc]initWithContentsOfURL:_audioRecorder.url];
NSMutableDictionary *myParam = [NSMutableDictionary dictionary];
[myParam setValue:#(nowTime) forKey:#"timestamp"];
[myParam setValue:audioFile forKey:#"file"];
[myParam setValue:api_key forKey:#"api_key"];
[myParam setValue:finalSignature forKey:#"signature"];
[cloudManager setUpDataCall:#"/upload" withParameters:myParam];
The setUpDataCall goes to a subclass of AFHTTPSessionManager with this code. This is the rest of the post URL if you were curious #"https://api.cloudinary.com/v1_1/karmo/raw"
- (void)setUpDataCall:(NSString *)callURL withParameters:(NSDictionary *)parameters {
[self POST:callURL parameters:parameters success:^(NSURLSessionDataTask *task, id responseObject) {
if ([self.delegate respondsToSelector:#selector(cloudManager:didReturnData:)]){
//Success :)
}
}failure:^(NSURLSessionDataTask *task, NSError *error) {
if ([self.delegate respondsToSelector:#selector(cloudManager:didFailWithError: )]) {
//Error :(
}
}];
}
The Error
I get this error when I try to send the file Invalid type in JSON write (NSConcreteData).
When I add an exception breakpoint it identifies this
[mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]];
Which is in this method in the AFURLRequestSerialization.m class.
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
I have seen a few other stackoverflow questions like the one below but I am not sure if those are still relevant as the code from those posts seem to throw errors in the code as I am guessing AFNetworking has been updated since those posts.
AFNetworking Uploading a file
Any code help or examples would be greatly appreciated. Thanks a bunch for taking the time.
Have you tried the suggestions as explained in the following post's comments?
Error with NSJSONSerialization - Invalid type in JSON write (Menu)
I would like to find out if it's possible to avoid duplicate HTTP requests with AFNetworking. Specifically, my app may generate multiple HTTP requests which all have the same url. I would like to prevent AFNetworking from processing duplicates of the same url.
Im not sure if this can be done in AFNetworking or the underlying iOS sdk. I understand that i could manually keep trac of pending url request and avoid duplicates that way, but was wondering if there is a lower level functionality already available to take care of this.
Thanks.
Your best bet is to subclass AFHTTPRequestOperationManager's HTTP request operations and keep track of them there if you want to track requests the same way for each request, otherwise the logic will need to be elsewhere.
AFNetworking doesn't support this because there is probably some logic relevant to when you should and when you should not execute a duplicate request, which would be highly customizable (not generic enough for the framework)
I made a category that checks for in-progress GET requests before making new ones.
https://github.com/NSElvis/AFHTTPSessionManager-AFUniqueGET
It does this by using the method getTasksWithCompletionHandler of the session.
I had the same problem. I have a chat-application and I need to show user avatar for each message. So I made few same requests and I've resolved this issue.
First, I add NSDictionary with NSString avatar URLs keys and completion blocks objects:
#property (strong, nonatomic) NSMutableDictionary* successBlocksDictForGetAvatar;
And here's my method to get user avatar image:
- (void)getAvatarForUser:(ETBUser*)user
completion:(void(^)())completionBlock
{
if (user.avatarURL)
{
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:user.avatarURL]];
if (self.successBlocksDictForGetAvatar[user.avatarURL])
[self.successBlocksDictForGetAvatar[user.avatarURL] addObject:completionBlock];
else
{
NSMutableSet* set = [[NSMutableSet alloc] initWithObjects:completionBlock, nil];
[self.successBlocksDictForGetAvatar setObject:set forKey:user.avatarURL];
AFHTTPRequestOperation* operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
UIImage* avatarImage = [UIImage imageWithData:responseObject];
if (avatarImage)
{
user.avatar = avatarImage;
[[DataManager shared] saveAvatar];
[((NSSet*)self.successBlocksDictForGetAvatar[user.avatarURL]) enumerateObjectsUsingBlock:^(void(^successBlock)(), BOOL *stop) {
successBlock();
}];
[self.successBlocksDictForGetAvatar removeObjectForKey:user.avatarURL];
}
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[self.successBlocksDictForGetAvatar removeObjectForKey:user.avatarURL];
}];
[self.manager.operationQueue addOperation:operation];
}
}
}
Here I check if my dictionary contains request. If YES, I add completion block for user in dictionary. Otherwise I setObject:forKey: and make AFNetworking request. In success and fail blocks I clean my dictionary.
P.S. Here's my manager getter:
- (AFHTTPRequestOperationManager*)manager
{
if (!_manager)
{
_manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:kBaseURL];
[_manager.requestSerializer setValue:NetworkConstantsHeaderAcceptValue forHTTPHeaderField:NetworkConstantsHeaderAcceptKey];
[_manager.operationQueue setMaxConcurrentOperationCount:1];
}
return _manager;
}
I am having a problem with AFNetworking.
I am requesting a lot of JSON data from my Server via GET using this:
[[SessionResponseClient sharedClient] getPath:kURIRateList parameters:#{...} success:^(AFHTTPRequestOperation *operation, id JSON) {
[_delegate receivedRateResponse:JSON];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[_delegate receivedRateResponse:nil];
}];
When this is called around 10-20 times, CFData seems to take most of the used memory of my app.
Memory Usage
When I investigate both CFData and CFData (store), I get the following:
CFData (store)
CFData
Side note: My app is using ARC and everything else seems to perform fine.
My question is now, as I didn't see any pending issues on AFNetworking's GitHub Page and almost no other complaints about that on the internet, what the heck am I doing wrong? Is anybody else facing this issue or am I the only one? Could you please post your code, as I don't think my Code looks that wrongs...
Update 1
The code for interface:
#protocol RateRequestsDelegate;
#interface RateRequests : NSObject {
id<RateRequestsDelegate> _delegate;
}
#property (nonatomic, strong) id<RateRequestsDelegate> delegate;
- (void)fetchRateList;
#end
#protocol RateRequestsDelegate <NSObject>
- (void)receivedRateResponse:(NSDictionary *)response;
#end
SessionResponseClient is just an extended AFHtttpClient instance, as all the AFNetworking examples demonstrate, see: Interact with an API
The code for receivedRateResponse:
- (void)receivedRateResponse:(NSDictionary *)json {
if (!json) {
return;
}
self.moviesToInsert = [NSMutableArray arrayWithArray:[json objectForKey:#"rated"]];
[self.tableView reloadData];
}
Update 2
I changed the callback now to a block-based function instead of using delegates and it improved a little, but its barely mentionable and could also be caused by just a side effect of something else. How do you guys implement this? Try fetching data, display it in a table- or scrollview and then pull these data many times from the server and refresh the views
Many thanks! Paul
Thanks dude - I had the exact same problem - your solution worked.
However, I thought I'd comment that in your solution, you're essentially turning the NSURLCache off once you receive a memory warning. I'm not sure exactly how AFNetworking uses the NSURLCache, but I'm assuming their implementation won't work as effectively if you turn it off (i.e. by setting its capacity to 0). This may slow down the request speed after your first memory warning onwards. As an extreme edge case, you might even experience unexpected behaviour after a few days of using your app, as it might not manifest until the first memory warning.
I instead suggest implementing something that removes the cached responses, but keeps the cache active:
-(void)didReceiveMemoryWarning
{
[[NSURLCache sharedURLCache] removeAllCachedResponses];
}
The only issue is that AFNetworking may need to build up its cache again, but I'm assuming this won't affect the framework quite as much.
Lastly, just in case it's useful to you or anyone - in order to register your own method to a memory warning notification, you can do this:
[[NSNotificationCenter defaultCenter] addObserver:self (or pointer to your singleton)
selector:#selector(myMethod)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
I hope that helps!
Okay, the solution was that CFData (store) seems to be responsible for holding the url cache in memory when using NSURLConnection's.
So all I had to do was call...
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];
[NSURLCache setSharedURLCache:sharedCache];
...to clear the cache, once I received a memory warning...
Or you can just call it whenever you want(didn't time profile that function though)...
Hope this helps someone who encounters a similar problem.
Maybe you could give #autoreleasepool a try.
For example:
#autoreleasepool {
// your codes below
[[SessionResponseClient sharedClient] getPath:kURIRateList parameters:#{...} success:^(AFHTTPRequestOperation *operation, id JSON) {
[_delegate receivedRateResponse:JSON];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[_delegate receivedRateResponse:nil];
}];
// your codes above
}
I am working on an iOS application that will use RestKit 0.20 to make REST-based calls to a service that is running on JBoss AS 7.1.1 and using restEASY as its REST-based web service framework.
The REST service that the client app will be calling is used to retrieve objects based on their unique identifier. Since these objects can be small or large (> 1MB in size) and great in number (20? 50? 100 or more at a time) I don't want to make one large call to retrieve them all at once. Rather, I was planning on using RestKit's queued operation support to create a GET request for each object based on the object identifier, and execute the calls asynchronously. Once the GET has completed, each object will be processed through the use of Objective-C blocks so as to avoid any unnecessary blocking.
My RestKit client code looks like this...
NSArray *identifiers = ...
RKObjectManager *objectManager = [RKObjectManager sharedManager];
RKResponseDescriptor *getObjResp = [RKResponseDescriptor responseDescriptorWithMapping:[MyObject mapping] pathPattern:[WebServiceHelper pathForServiceOperation:#"/objects/:uniqueIdentifier"] keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
for (int i=0; i < identifiers.count; i++) {
NSString *identifier = [identifiers objectAtIndex:i];
NSURL *serviceURL = [WebServiceHelper urlForServiceOperation:[NSString stringWithFormat:#"/objects/%#", identifier]];
NSURLRequest *request = [NSURLRequest requestWithURL:serviceURL];
RKObjectRequestOperation *requestOp = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors:#[getObjResp]];
[requestOp setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
MyObject *obj = [mappingResult firstObject];
if (self.delegate != nil) {
[self.delegate didLoadObjectWithIdentifier:identifier myObj:obj];
}
} failure:^(RKObjectRequestOperation *operation, NSError *error){
if (self.delegate != nil) {
[self.delegate didFinishWithError:error];
}
}];
[objectManager enqueueObjectRequestOperation:requestOp];
}
From there, the delegate method that gets called when an object has been retrieved looks like this:
-(void)didLoadObjectWithIdentifier:(NSString *)identifier myObj:(MyObject *)myObj {
if(secureMessage != nil) {
NSLog(#"Message %# retrieved successfully : %#:%#", identifier, myObj);
} else {
NSLog(#"NO OBJ");
}
}
The calls appear to be functioning as expected, as I am able to print out information about the retrieve objects. However, I am seeing some weird/unexepcted behavior on the service side.
First, I see a number of Exceptions being thrown by restEASY:
13:22:02,903 WARN [org.jboss.resteasy.core.SynchronousDispatcher] (http--0.0.0.0-8080-10) Failed executing GET /objects/BBFE39EA126F610C: org.jboss.resteasy.spi.WriterException: ClientAbortException: java.net.SocketException: Broken pipe
at org.jboss.resteasy.core.ServerResponse.writeTo(ServerResponse.java:262) [resteasy-jaxrs-2.3.2.Final.jar:]
at org.jboss.resteasy.core.SynchronousDispatcher.writeJaxrsResponse(SynchronousDispatcher.java:585) [resteasy-jaxrs-2.3.2.Final.jar:]
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:506) [resteasy-jaxrs-2.3.2.Final.jar:]
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:119) [resteasy-jaxrs-2.3.2.Final.jar:]
at org.jboss.seam.resteasy.ResteasyResourceAdapter$1.process(ResteasyResourceAdapter.java:145) [jboss-seam-resteasy.jar:2.3.0.Final]
at org.jboss.seam.servlet.ContextualHttpServletRequest.run(ContextualHttpServletRequest.java:65) [jboss-seam.jar:2.3.0.Final]
at org.jboss.seam.resteasy.ResteasyResourceAdapter.getResource(ResteasyResourceAdapter.java:120) [jboss-seam-resteasy.jar:2.3.0.Final]
...
It would appear as though RestKit is closing the socket somehow (or some other error is preventing the object from being read from the server). I am unable to find anything in the documentation that could explain what is going on here.
Secondly, though, I also see another call for the very same object when a request fails with this error. Why is the GET being called more than once? Is RestKit redoing the failed GET request?
I'm mostly concerned about why the Exception is occurring within restEASY, as it will make it difficult to diagnose calls that really do fail. Has anyone seen this behavior before? Any tips as to how I can correct these issues? Thanks for any help you can give!!
Those exception are resulted from disconnected Clients i.e. some of the users might quit the app while waiting for the process to complete OR has a network failure (at the client end).
Hence, Broken Pipe.
Hi I am new to AFNetworking library and am integrating it for first time in my iPAD app. However I am having hard time finding a working example for reference.
I found the following solution by rckoenes here AFNetworking POST and get Data back. However I am hard time implementing it. Since that answer is already marked as correct. I am assuming the library as changed since that time and the example might not be relevant.
Any help with a working example reference is highly appreciated.
NSURLRequest *request = [wsu generateURLRequest:getFavAPI method : #"GET" arguments:nil eventName:USER_FAVS_SUCCESS_NOTIF contentType:nil];
NSLog(#"Request %#", request.description);
AFXMLRequestOperation *favOpertion = [[AFXMLRequestOperation alloc] initWithRequest:request];
//favOpertion.responseXMLParser = [NSXMLParser initialize]; // error: setting the readOnly property.
[favOpertion setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *favOpertion, id responseObject) {
NSLog(#"Response: %#",[favOpertion responseString]); // Never reached here
} failure:^(AFHTTPRequestOperation *favOpertion, NSError *error) {
NSLog(#"Error: %#",[favOpertion error]); // never reached here
}];
The code prints the request desc as valid: http://[server name]/favorites/folders/user/[uid]>
Here is an example of the default SAX style parsing. There are some other XML request operations people have made that give you an actual XML document:
AFKissXMLRequestOperation
AFGXMLRequestOperation