I have an iOS project with ARC enabled and i'm using Leaks instrument to find leaks.
i have one leak in this line of code and i don't understand why it's a leak :
[self.activeDownload appendData:data];
this is the code of the class
#interface IconDownloader ()
#property (nonatomic, strong) NSMutableData *activeDownload;
#property (nonatomic, strong) NSURLConnection *imageConnection;
#end
#implementation IconDownloader
#synthesize url = _url;
#synthesize activeDownload = _activeDownload;
#synthesize imageConnection = _imageConnection;
- (void)startDownload
{
self.activeDownload = [NSMutableData data];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:self.url]];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
self.imageConnection = conn;
}
- (void)cancelDownload
{
[self.imageConnection cancel];
self.imageConnection = nil;
self.activeDownload = nil;
self.completionHandler = nil;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.activeDownload appendData:data]; // leak
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.activeDownload = nil;
self.imageConnection = nil;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (self.completionHandler) {
self.completionHandler(self.activeDownload);
}
self.imageConnection = nil;
}
Related
So, I'm following this answer in trying to pass data between two classes in an iOS project without storyboards. I'm trying to abstract all my server calls in a different class, so the ViewControllers don't get too cramped. I have an OfferRequest class, which handles the server requests and declares the protocol:
#protocol OfferRequestDelegate <NSObject>
-(void)addOfferRequest: (OfferRequest *)request dataFromRequest: (NSMutableArray *)data;
#end
#interface OfferRequest : NSObject <LocationManagerDelegate, NSURLConnectionDataDelegate>
#property (nonatomic, weak) id <OfferRequestDelegate> delegate;
#end
The idea is to call addOfferRequest: dataFromRequest in connectionDidFinishLoading, set the data as the result from the request and pass it on to the ViewController.
In OfferRequest.m:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSMutableArray *result = [NSJSONSerialization JSONObjectWithData:receivedData options:kNilOptions error:nil];
[self.delegate addOfferRequest:self dataFromRequest:result];
}
I declare my ViewController as a delegate for the OfferRequest:
#interface CategorySuggestionViewController : UIViewController <OfferRequestDelegate>
Initialize the OfferRequest object and set it's delegate:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
offer = [OfferRequest new];
offer.delegate = self;
}
And then implement the delegate method:
-(void)addOfferRequest:(OfferRequest *)request dataFromRequest:(NSMutableArray *)data {
NSLog(#"received data = %#", data);
}
But it never gets called. What am I missing ?
try this...
your OfferRequest class like below i have given name it as connectionClass
connectionClass.h
#protocol connectionDelegate <NSObject>
-(void)connectionSucceed:(NSData *)data;
-(void)connectionFailed:(NSError *)error;
#end
#import <Foundation/Foundation.h>
#interface connectionClass : NSObject
{
id<connectionDelegate> delegate;
NSMutableData *receivedData;
NSString *encodedString;
NSMutableURLRequest *theRequest;
NSURL *url;
NSString *length;
NSURLConnection *Connect;
}
#property(nonatomic, retain) id<connectionDelegate> delegate;
#property(nonatomic, retain) NSMutableData *receivedData;
- (void)connectionWithURL:(NSString *)URL;
#end
connectionClass.m
#import "connectionClass.h"
#implementation connectionClass
#synthesize delegate, receivedData;
- (void)connectionWithURL:(NSString *)URL
{
url=[[NSURL alloc] initWithString:[self urlencode:URL]];
theRequest= [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60];
[theRequest setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
length = [NSString stringWithFormat:#"%lu", (unsigned long)[URL length]];
[theRequest setValue:length forHTTPHeaderField:#"Content-Length"];
[theRequest setHTTPMethod:#"POST"];
[theRequest setHTTPBody:[URL dataUsingEncoding:NSUTF8StringEncoding]];
Connect = [NSURLConnection connectionWithRequest:theRequest delegate:self];
if (Connect)
{
self.receivedData = [NSMutableData data];
}
else
{
NSError *error;
[self.delegate connectionFailed:error];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[self.delegate connectionSucceed:self.receivedData];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[self.delegate connectionFailed:error];
}
-(NSString *) urlencode: (NSString *) string
{
encodedString = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)string, NULL, (CFStringRef)#"", kCFStringEncodingUTF16));
return encodedString;
}
#end
don't forget to import your connectionclass and delegate
#interface yourclass : UIViewController<connectionDelegate>
Initialize the OfferRequest object and set it's delegate:
-(void)viewWillAppear:(BOOL)animated
{
connection = [[connectionClass alloc]init];
connection.delegate = self;
[connection connectionWithURL:[NSString stringWithFormat:#"pass your url here"]];
}
The delegate method will have your values
-(void)connectionSucceed:(NSData *)data
{
NSError *error;
self.dictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
//nslog your data
}
//Connection failure
-(void)connectionFailed:(NSError *)error
{
//handle error message
}
Ok, I don't know where the problem exactly was, but what I did to fix it, was to create a new init method, where I pass the delegate:
-(instancetype)initWithDelegate: (id<OfferRequestDelegate>) delegate {
self = [super init];
if (self) {
self.delegate = delegate;
}
return self;
}
So I initialize the OfferRequest object in the VC and pass self and now it works.
I guess delegate should be a pointer object and not the object.
It may be help you
#property (nonatomic, weak) NSObject <OfferRequestDelegate> *delegate;
and make should your delegate is been set, i.e. make sure your code below is getting called.
[self.delegate addOfferRequest:self dataFromRequest:result];
I am developing a framework which requires error callbacks. I have used delegate methods to give error callbacks to the users(developer) of the framework. Can I use NSError to indicate error details in the delegate method or simply use NSDictionary instead ? Are there any memory issues concerned here?
Instead of delegate methods, you should use blocks with proper error handling, something like this:
Interface:
typedef void(^ResultBlock)(id result, NSError *error);
extern NSString * const DummyErrorDomain;
enum{
DummyErrorReason1,
DummyErrorReason2
};
#interface Dummy : NSObject
-(void)doSomething:(ResultBlock)resultBlock;
#end
Implementation with async NSURLConnection:
NSString * const DummyErrorDomain = #"DummyErrorDomain";
#interface Dummy ()
#property (strong) ResultBlock resultBlock;
#property NSURLConnection *connection;
#property NSMutableData *responseData;
#end
#implementation Dummy
-(void)doSomething:(ResultBlock)resultBlock
{
self.resultBlock = resultBlock;
self.responseData = [[NSMutableData alloc] init];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:#"dummyURL"]];
self.connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
self.resultBlock(nil,error);
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
self.resultBlock(self.responseData, nil);
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[self.responseData appendData:data];
}
#end
Usage:
-(void)usage
{
Dummy *dummy = [[Dummy alloc] init];
[dummy doSomething:^(id result, NSError *error) {
if(result){
// everything went fine
}else{
// error handling
}
}];
}
My app is downloading a file from the internet using NSURLConnection.
Two problems are happening:
1) [connection didReceiveData] is never called
2) When [connection didFinishDownloading] is called, my self.currentData is empty
What am I doing wrong?
My header looks like this:
#interface DIFileDownloader : NSObject <NSURLConnectionDelegate> {
NSMutableData *currentData;
}
#property (nonatomic, assign) id <DIFileDownloaderDelegate> delegate;
#property (nonatomic, retain) NSMutableArray *supportedFormats;
#property (nonatomic, retain) NSMutableData *currentData;
#property (nonatomic, retain) NSURLConnection *downloadAgent;
#property (nonatomic) long long expectedContentSize;
#property (nonatomic) float currentDownloadProgress;
#property (nonatomic, getter = isRunning) BOOL running;
-(instancetype)initWithSupportedFormats:(NSArray *)extensions;
-(void)downloadFileAtURL:(NSURL *)url;
-(NSString *)documentsDirectoryPath;
#end
And my implementation file:
-(instancetype)initWithSupportedFormats:(NSArray *)extensions {
self.supportedFormats = [[NSMutableArray alloc] initWithArray:extensions];
self.running = NO;
}
-(void)downloadFileAtURL:(NSURL *)url {
NSString *fileExtension = [url pathExtension];
if ([self.supportedFormats containsObject:fileExtension]) {
self.downloadAgent = [[NSURLConnection alloc] initWithRequest:[NSURLRequest requestWithURL:url] delegate:self];
[self.downloadAgent start];
NSLog(#"Beginning download at URL:%#", url.absoluteString);
}
else {
}
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"Connection recieved response with size: %f", (float)[response expectedContentLength]);
self.expectedContentSize = (float)[response expectedContentLength];
self.running = YES;
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
NSLog(#"%i", [httpResponse statusCode]);
/*
if ([httpResponse statusCode] >= 200 && [httpResponse statusCode] <= 299) {
}
*/
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"%f", (float)[data length]);
[currentData appendData:data];
self.currentDownloadProgress = ((float)[data length] / self.currentDownloadProgress);
NSLog(#"Connection recieved data. New progress is: %f", self.currentDownloadProgress);
if ([self.delegate respondsToSelector:#selector(downloader:progressChanged:)]) {
[self.delegate downloader:self progressChanged:self.currentDownloadProgress];
}
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"Connection failed");
if ([self.delegate respondsToSelector:#selector(downloader:failedToDownloadFileAtURL:reason:)]) {
[self.delegate downloader:self failedToDownloadFileAtURL:connection.originalRequest.URL reason:error];
}
}
-(void)connectionDidFinishDownloading:(NSURLConnection *)connection destinationURL:(NSURL *)destinationURL {
NSLog(#"Connection finished downloading with size: %f", (float)[currentData length]);
self.running = NO;
NSString *filename = [destinationURL.absoluteString lastPathComponent];
NSString *docPath = [self documentsDirectoryPath];
NSString *pathToDownloadTo = [NSString stringWithFormat:#"%#/%#", docPath, filename];
NSError *error = nil;
[currentData writeToFile:pathToDownloadTo options:NSDataWritingAtomic error:&error];
if (error != nil) {
NSLog(#"DIFileDownloader: Failed to save the file because: %#", [error description]);
if ([self.delegate respondsToSelector:#selector(downloader:failedToDownloadFileAtURL:reason:)]) {
[self.delegate downloader:self failedToDownloadFileAtURL:connection.originalRequest.URL reason:error];
}
}
else {
if ([self.delegate respondsToSelector:#selector(downloader:finishedDownloadingFileNamed:atPath:)]) {
[self.delegate downloader:self finishedDownloadingFileNamed:filename atPath:pathToDownloadTo];
}
}
}
- (NSString *)documentsDirectoryPath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectoryPath = [paths objectAtIndex:0];
return documentsDirectoryPath;
}
#end
As mentioned in others and in my comment, there are a few bugs in your code which you need to fix.
But there is also one big misconception - stemming from the former awful documentation of NSURLConnection:
connectionDidFinishDownloading:destinationURL: is NOT a delegate method of the two delegates of NSURLConnection namely, NSURLConnectionDelegate and NSURLConnectionDataDelegate which you are supposed to implement in your case.
The minimal set of delegate methods you need to implement are:
For the NSURLConnectionDelegate:
(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
then for the NSURLConnectionDataDelegate:
(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
(void)connectionDidFinishLoading:(NSURLConnection*)connection;
If you don't mind, I've put a sample of a minimal implementation on Gist: SimpleGetHTTPRequest which should give you a jump start do implement your own HTTP request class.
I'm writing simple code which is sending me response from http server after sending a POST request to it but if I try moving everything to another file XCode shows me this error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'data parameter is nil'
Here are the two files I'm using to make this work:
#import "HTTPRequestHandler.h"
#interface HTTPRequestHandler ()
#property (strong, nonatomic) NSURL *requestURL;
#property (strong, nonatomic) NSData *httpBody;
#property (strong, nonatomic) NSMutableData *responseData;
#end
#implementation HTTPRequestHandler
- (id)initWithURL:(NSString *)url body:(NSString *)body
{
if (self = [super init]) {
self.requestURL = [[NSURL alloc] initWithString:url];
self.httpBody = [body dataUsingEncoding:NSUTF8StringEncoding];
}
return self;
}
- (void)makeConnectionWithRequest
{
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:self.requestURL];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:self.httpBody];
[NSURLConnection connectionWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"Dane doszły 1");
[self.responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"Dane doszły 2");
[self.responseData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Dane doszły 3");
NSDictionary *recievedData = [NSJSONSerialization JSONObjectWithData:self.responseData options:kNilOptions error:nil];
NSLog(#"%#", [recievedData description]);
}
#end
and the second one:
#import "ViewController.h"
#import "HTTPRequestHandler.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
HTTPRequestHandler *request = [[HTTPRequestHandler alloc] initWithURL:#"http://own-dev1.railwaymen.org:4006/api/get_grant_key"
body:#"email=vergun#gmail.com&password=password"];
[request makeConnectionWithRequest];
}
#end
Okey, got it! Just forgot self.responseData = [NSMutableData data];
I'm trying to use googletts converted and seem to be running into trouble. I think I made the class correctly in terms of structure. But I dont think the NSURLConnection is returning any useful data. Can someone please help me check if its working and if not why?
In the connectiondidfinishloading section I am checking download length
NSLog(#"hmmmm %lu", (unsigned long)[downloadedData length])
which is returning 0. Thats why I think its not working.
The goal is to have the app speak what is downloaded
Thanks!!!
I just have a button in my view controller that launches everything
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import "RJGoogleTTS.h"
#interface ViewController : UIViewController <CLLocationManagerDelegate>
#property (weak, nonatomic) IBOutlet UILabel *userLabel;
#property (strong, nonatomic) CLLocationManager *locationManager;
#property (strong, nonatomic) CLGeocoder *geoCoder;
#property (strong, nonatomic) RJGoogleTTS *googleTTS;
- (IBAction)geoCodeLocation:(id)sender;
- (IBAction)rjGoogleButton:(id)sender;
#end
here is the implementation file
- (IBAction)rjGoogleButton:(id)sender {
googleTTS = [[RJGoogleTTS alloc]init];
[googleTTS convertTextToSpeech:#"How are you today user?"];
}
RJGoogleTTS.h
#import <Foundation/Foundation.h>
#protocol RJGoogleTTSDelegate <NSObject>
#required
- (void)receivedAudio:(NSMutableData *)data;
- (void)sentAudioRequest;
#end
#interface RJGoogleTTS : NSObject {
id <RJGoogleTTSDelegate> delegate;
NSMutableData *downloadedData;
}
#property (nonatomic, retain) id <RJGoogleTTSDelegate> delegate;
#property (nonatomic, retain) NSMutableData *downloadedData;
- (void)convertTextToSpeech:(NSString *)searchString;
#end
.m
#import "RJGoogleTTS.h"
#import <AVFoundation/AVFoundation.h>
#implementation RJGoogleTTS
#synthesize delegate, downloadedData;
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
- (void)convertTextToSpeech:(NSString *)searchString {
NSString *search = [NSString stringWithFormat:#"http://translate.google.com/translate_tts?q=%#", searchString];
search = [search stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
NSLog(#"Search: %#", search);
self.downloadedData = [[NSMutableData alloc] initWithLength:0];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:search]];
[request setValue:#"Mozilla/5.0" forHTTPHeaderField:#"User-Agent"];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
[delegate sentAudioRequest];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"did you receive response user");
[self.downloadedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"did you receive data user");
[self.downloadedData appendData:data];
// AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc]initWithData:downloadedData error:nil];
// [audioPlayer play];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"Failure");
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"it worked user!!!");
[delegate receivedAudio:self.downloadedData];
NSLog(#"hmmmm %d", [self.downloadedData length]);
// NSString *txt = [[NSString alloc] initWithData:downloadedData encoding: NSASCIIStringEncoding];
// NSLog(#"%#hello",txt);
}
#end
Remember that this is happening asynchronously. You set the size of downloadedData to 0 after your call the NSURLConnection.
Move:
self.downloadedData = [[NSMutableData alloc] initWithLength:0];
before your call to NSURLConnection.
There's nothing wrong with your NSURLConnection methods. Your search string is wrong. It should be:
NSString *search = [NSString stringWithFormat:#"http://translate.google.com/translate_tts?tl=en&q=%#",searchString];
You didn't start the URLConnection. Change [[NSURLConnection alloc] initWithRequest:request delegate:self]; like this [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];