"message sent to deallocated instance" error with AFNetworking - ios

So when using "AFNetworking" in my project, i tried the very basic examples just to make sure it's working but i keep getting the following error:
[NSConcreteMutableData appendData:]: message sent to deallocated instance 0x83aa8030
My code is:
NSDictionary *params = #{#"username": username,
#"password": password,
#"comment_id": comment_id]};
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc]initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[manager POST:url parameters:params progress:nil success:^(NSURLSessionTask * _Nonnull task, id _Nullable responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(NSURLSessionTask * _Nullable operation, NSError * _Nonnull error) {
NSLog(#"Error: %#", error);
}];
The error occurs in the following function which is part of script: ([AFURLSessionManagerTaskDelegate URLSession:dataTask:didReceiveData:] AFURLSessionManager.m:262)
#pragma mark - NSURLSessionDataDelegate
- (void)URLSession:(__unused NSURLSession *)session
dataTask:(__unused NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data{
self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive;
self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived;
[self.mutableData appendData:data];}

Try to store an instance of AFHTTPSessionManager in your class as a property. Something like:
#property (nonatomic, strong) AFHTTPSessionManager *manager;
Then you should not see an error about the deallocated object, hopefully!
Else please post more context to the problem, so that I can help you with this.

Related

AFNetworking causes crash at response

Crash ScenarioI am using AFNetworking for GET and POST requests and I am calling GET and POST methods on MAIN QUEUE and when the response comes,I update the UI.Now,before the response comes from API I am pushing onto another ViewController,and that's when the crash occurs.The message says:bad_accessPossible SolutionShould I be calling that method on some background queue so that I Can update that on MAIN QUEUE.Is it correct? Here is the code:
-(void)getDataFromUrl:(NSString *)url withRequestName:(NSString *)requestName withMessege:(NSMutableDictionary *)message
{
Reachability* googleReach = [Reachability reachabilityWithHostName:#"www.google.com"];
if(googleReach.currentReachabilityStatus!=0)
{
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:url parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(#"output :%#", responseObject);
arrayParsedJson = (NSMutableArray * )responseObject;
[self.delegate dataReceivedFromService:arrayParsedJson withRequestName:requestName];
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[self.delegate dataReceivedErrorService:error withRequestName:requestName withMsg:error.description];
}];
}
else
{
//[TSMessage showNotificationInViewController:views title:Title_Alert subtitle:Service_Alert type:TSMessageNotificationTypeError];
}
}
So when I get response in the success block,I call my delegate methods you can see.But if I have navigated to some other viewController before the response comes in block, it crashes.

Get Data from AFHTTPSessionManager

Since I am new to IOS and AFNetworking 3,0 is new, I don't know how to retrieve data from AFHTTPSessionManager.
I have to following message and I want to return the result
- (NSString *) makeServiceCall;
{
NSString *response = #"";
#try {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager
POST:self.url.absoluteString
parameters:self.parameters
progress:nil
success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(#"Success: %#", responseObject);}
failure:^(NSURLSessionDataTask * task, NSError * error) {
NSLog(#"Error: %#", error);
}];
[AFHTTPSessionManager manager].securityPolicy.allowInvalidCertificates = YES;
}
#catch (NSException *exception) {
NSLog(#"%#", exception.reason);
}
}
The method AFHTTPSessionManager POST:parameters:progress:success:failure: is an asynchronous method.
What you are trying to do is return a string from the method calling it. This will not work as the method will finish before the download has started.
You need to call this with a completion block something like this...
- (void)getStringWithCompletionHandler:(void (^)(id))completion {
NSLog(#"Method started");
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager
POST:self.url.absoluteString
parameters:self.parameters
progress:^(NSProgress * _Nonnull uploadProgress) {
NSLog(#"Download underway");
}
success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(#"Download successful");
completion(responseObject);
}
failure:^(NSURLSessionDataTask * task, NSError * error) {
NSLog(#"Error");
}];
// trying to return a string here won't work because the download hasn't finished yet.
// You can see the order of things happening by adding logs...
NSLog(#"Method finished");
}
The order of the logs in this code will be...
Method started
Method finished
Download underway
Download successful
As you can see, trying to return at the end of the method won't work because the download won't have completed yet.

Wanting to use the data I get back when using AFNetworking

I am using AFNetworking to get a JSON response. I am getting is as a PhotoPXArray (model I created using mantle). The log output is exactly the data I want. My problem is using the data. How do I go about saving the response data as a variable that can be used elsewhere in my program.
Also, I am using Sculptor to help with serializing.
-(NSArray*) getPhotoForWord:(NSString*)word {
NSArray *results = nil;
NSString *requestString = BASE_URL;
requestString = [requestString stringByAppendingString:#"photos/search?term="];
requestString = [requestString stringByAppendingString:word];
requestString = [requestString stringByAppendingString:CONSUMER_KEY];
NSString *encoded = [requestString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [SCLMantleResponseSerializer serializerForModelClass:PhotoPXArray.class];
[manager GET:encoded
parameters:nil
//success:^(AFHTTPRequestOperation *operation, id responseObject) {
success:^(AFHTTPRequestOperation *operation, PhotoPXArray *responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
return results;
}
#end
Read the Apple documentation regarding blocks and variables. Or you can view this question on SO that will probably also answer your question.
From the Apple docs:
__block variables live in storage that is shared between the lexical scope of the variable and all blocks and block copies declared or
created within the variable’s lexical scope. Thus, the storage will
survive the destruction of the stack frame if any copies of the blocks
declared within the frame survive beyond the end of the frame (for
example, by being enqueued somewhere for later execution). Multiple
blocks in a given lexical scope can simultaneously use a shared
variable.
Use a completion block to get your data out:
- (void)getPhotoForWord:(NSString *)word completionHandler:(void ^(PhotoPXArray *photoArray))completionHandler
{
NSString *requestString = BASE_URL;
requestString = [requestString stringByAppendingString:#"photos/search?term="];
requestString = [requestString stringByAppendingString:word];
requestString = [requestString stringByAppendingString:CONSUMER_KEY];
NSString *encoded = [requestString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [SCLMantleResponseSerializer serializerForModelClass:PhotoPXArray.class];
[manager GET:encoded
parameters:nil
success:^(AFHTTPRequestOperation *operation, PhotoPXArray *responseObject) {
NSLog(#"JSON: %#", responseObject);
if (completionHandler) {
completionHandler(responseObject);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
Then call it like this:
[object getPhotoForWord:#"word" completionHandler:^(PhotoPXArray *photoArray) {
// Do something with photo array.
}];
Note that this call is asynchronous and will complete at some unknown time in the future. Also, you should likely take an NSError argument in the completion block so you can see if you get an error from the request, but I'll leave that to you.

Value of NSMutableDictionary is not changing inside the block

I am passing the URL in this method and getting the data as output. i want to assign a new value to nsmutabledictionary but it is not assigning the value.
-(NSDictionary*) getDatafromURL: (NSString*)url{
__block NSMutableDictionary *returnData=[[NSMutableDictionary alloc] init];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
returnData=(NSMutableDictionary*)responseObject;
NSLog(#"Data 1: %#",returnData);// it is printing the data
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
NSLog(#"Data 2: %#",returnData);// it is not printing any data
return returnData;
}
in this above example the Data 1 is showing value successfully
Data 2 gives me empty dictionary.why it is not assigning the new value?
That happens because you get to the line with "Data 2" first and the block is executed only afterwards, since it is an async request. I would suggest that you change your method to something like:
- (void)getDataFromURL:(NSString *)url completionHandler:(void (^)(NSMutableDictionary *returnData, NSError *error))handler {
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
returnData=(NSMutableDictionary*)responseObject;
NSLog(#"Data 1: %#",returnData);// it is printing the data
handler(returnData, nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
handler(nil, error);
}];
}
There might be some compile errors in the code I provided.
The other solution would be to do a synchronous request, in which case the block would be executed before the code that is after the block.
EDIT:
If you are choosing the first solution, you have to continue using it asynchronously. So you would call it like:
[self getDataFromURL:#"abc.com" completionHandler:^ (NSMutableDictionary *returnData, NSError *error) {
// process your dictionary and the error object
}];
Please check whether your Data 2 is printing before data 1? If yes, its because, the response object gets downloaded only after a certain delay. Take away the return statements. Pass the data to the dictionary to which you return the method. For eg: like
instead of
self.myDictionary = [self getDatafromURL:someURl];
to
-(void) getDatafromURL: (NSString*)url{
__block NSMutableDictionary *returnData=[[NSMutableDictionary alloc] init];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
returnData=(NSMutableDictionary*)responseObject;
NSLog(#"Data 1: %#",returnData);// it is printing the data
self.myDictionary = returnData;
// Continue whatever you want to do
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
Or use the dispatch methods instead of the blocks.
like
Or use manager waitUntilFinish method below.

How can I log each request/response using AFHTTPSessionManager?

I'm migrating my project to AFNetworking 2.0. When using AFNetworking 1.0, I wrote code to log each request/response in the console. Here's the code:
-(AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request
success:(void (^)(AFHTTPRequestOperation *, id))success
failure:(void (^)(AFHTTPRequestOperation *, NSError *))failure
{
AFHTTPRequestOperation *operation =
[super HTTPRequestOperationWithRequest:request
success:^(AFHTTPRequestOperation *operation, id responseObject){
[self logOperation:operation];
success(operation, responseObject);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error){
failure(operation, error);
}];
return operation;
}
-(void)logOperation:(AFHTTPRequestOperation *)operation {
NSLog(#"Request URL-> %#\n\nRequest Body-> %#\n\nResponse [%d]\n%#\n%#\n\n\n",
operation.request.URL.absoluteString,
[[NSString alloc] initWithData:operation.request.HTTPBody encoding:NSUTF8StringEncoding],
operation.response.statusCode, operation.response.allHeaderFields, operation.responseString);
}
I'm trying to do the same thing using AFNetworking 2.0, which to my understanding, means using a NSURLSessionDataTask object instead of AFHTTPRequestOperation. Here's my shot at it.
-(NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLResponse *, id, NSError *))completionHandler {
NSURLSessionDataTask *task = [super dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error){
[self logTask:task];
completionHandler(response, responseObject, error);
}];
return task;
}
-(void)logTask:(NSURLSessionDataTask *)task {
NSString *requestString = task.originalRequest.URL.absoluteString;
NSString *responseString = task.response.URL.absoluteString;
NSLog(#"\n\nRequest - %#\n\nResponse - %#\n\n", requestString, responseString);
}
The dataTaskWithRequest:completionHandler method is successfully intercepting each call, so I think that's the right method to override, but when I try to log the task in the completionHandler, task is nil. Thus getting nulls printed in the console. However a proper task object is still returned from that method. What's happening here? How can I properly log the request/response for each call?
you can use the library AFNetworking/AFNetworkActivityLogger
https://github.com/AFNetworking/AFNetworkActivityLogger
from the doc:
AFNetworkActivityLogger is an extension for AFNetworking 2.0 that logs
network requests as they are sent and received.
usage:
[[AFNetworkActivityLogger sharedLogger] startLogging];
output:
GET http://example.com/foo/bar.json
200 http://example.com/foo/bar.json
using devel logging level you should have responseHeaderFields and responseString too

Resources