every time i make a request to my web server via NSURLSession/Connection it leaks! To be honest it is not much but if you have to make a couple hundreds or thousands of calls this gets nasty.
I have been on this for about a week now. I have tried everything. NO cache , little cache, setting everything on nil after the call is done(which is unnecessary), using datatask for sessions or just connection with requests. Every time i get a little more memory allocated and i have not found a way to solve this problem.
So i set up a little testApp:
#import "ViewController.h"
#interface ViewController ()
#property NSString *param;
#property NSURL * url;
#property NSURLConnection *test;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.param = [NSString stringWithFormat:#"hi"];
self.url = [[NSURL alloc]initWithString:#"http://127.0.0.1/xmlfile.php"];
for (int i = 0; i<20000; i++){
[self connect:self.param url:self.url];
}
NSLog(#"I AM DONE!");
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSData *)connect:(NSString*)param url:(NSURL*)url{
self.test = [[NSURLConnection alloc]initWithRequest:nil delegate:self];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
return nil;
}
#end
i would love to add images of the memory usage but i am too new.
so just go here:
5k:iterations
http://i59.tinypic.com/123tts2.png
20k:
http://i59.tinypic.com/1zzqsrk.png
I have heard that this could be a problem with ios8.
Please help!
I am open for everything and would be happy if someone could prove me wrong and show me the right way. Thanks a bunch
You're never actually turning your URL into a request and giving it to the NSURLConnection. Because of that, the connection will never complete, and its memory will stay allocated. You'll find a world of difference with the above code if you add this to your connect:url: method:
NSURLRequest* request = [[NSURLRequest alloc] initWithURL:url];
...and pass that into your NSURLConnection so it actually does something:
self.test = [[NSURLConnection alloc]initWithRequest:request delegate:self];
...because then, as soon as the connection finishes doing its work—successfully or otherwise—its memory will be deallocated.
I have a background task to download from a webservice which runs in background and i want to suspend it if user navigates to other screens meanwhile.
Here is how i tried to download in background:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NewsResponseBO *objNewsRspnc = [objNewsParser getNewsStartingFrom:0 toEndLimit:10];
dispatch_async(dispatch_get_main_queue(), ^{
for (int i=0; i< [objNewsRspnc.arrNewsData count]; i++) {
[arrListOfNews addObject:[objNewsRspnc.arrNewsData objectAtIndex:i]];
}
isDataLoading = NO;
isBottomLoaderAdded = NO;
[loader stopAnimating];
[loader removeFromSuperview];
[bottomViewforLoader removeFromSuperview];
tbv_ListOfNews.frame = CGRectMake(tbv_ListOfNews.frame.origin.x, tbv_ListOfNews.frame.origin.y, tbv_ListOfNews.frame.size.width, tbv_ListOfNews.frame.size.height +80);
tbv_ListOfNews.contentOffset = CGPointMake(0, tbv_ListOfNews.contentSize.height);
[tbv_ListOfNews reloadData];
});
});
and Here is how i navigate on tablecell's selection:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (bottomViewforLoader != nil) {
tbv_ListOfNews.frame = CGRectMake(0, 44, 320, 416+APP_DELEGATE.floatExtraHeight) ;
[bottomViewforLoader removeFromSuperview];
}
[tableView deselectRowAtIndexPath:indexPath animated:NO];
NewsDetailViewController *obj_DetailNews = [[NewsDetailViewController alloc]initWithNibName:nil bundle:[NSBundle mainBundle]];
NSLog(#"indexPath.row:%d,arrListOfNews(number of object:%d) ",indexPath.row,[arrListOfNews count]);
obj_DetailNews.obj_newsDetail = [arrListOfNews objectAtIndex:indexPath.row];
[APP_DELEGATE.navController pushViewController:obj_DetailNews animated:YES];
}
any help on how to suspend dispatch_get_global_queue ?
Thanks in Advance.
You cannot suspend/resume the global concurrent queues. Furthermore, the code you've posted appears to execute synchronously. You will not be able to cancel it without modifying it to be be "cancelable" (i.e. -getNewsStartingFrom:toEndLimit: will need to check a variable somewhere indicating cancellation and voluntarily stop itself.) I don't know if you own NewsResponseBO or not, but just off the top of my head, this sound like a good job for NSURLConnection and a concurrent NSOperation owing to NSOperationQueue/NSOperation's support for cancellation (but note that NSOperations still don't get you hard cancellation).
See my answer to this question for more detail on cancellation of pending dispatch jobs and why hard cancellation isn't possible.
This is a little strange, but I have been trying to find a solution for 2 days straight, to set the map region but nothing seems to work
Here is my code :
-(void)viewWillAppear:(BOOL)animated{
//[self step1locationupdate];
Maplocation.showsUserLocation=YES;
MKCoordinateRegion mapRegion;
mapRegion.center.longitude=self.currentProduct.shop.longcoordinate;
mapRegion.center.latitude=self.currentProduct.shop.latcoordinate;
NSLog(#"The USer Location are :%f",mapRegion.center.latitude);
mapRegion.span.latitudeDelta= 2;
mapRegion.span.longitudeDelta= 2;
[Maplocation setRegion:mapRegion animated:YES];
NSLog(#"The USer Location are :%f %f",Maplocation.userLocation.coordinate.latitude,Maplocation.userLocation.coordinate.longitude);
}
The NSLog for malocation.userlocation.coordinate are always 0 when it starts.
The same code I have added it into the viewDidLoad part & also there was no difference
Kindly help
I think you need to initialize your MKCoordinateRegion before you start using it. Something like 'MKCoordinateRegion mapRegion = MKMakeRegionWithCoordinates(......'
Well I have saved the above method in a (void) method , then I have fired it after 5 Sec from viewWillAppear method.
-(void)viewWillAppear:(BOOL)animated{
[self performSelector:#selector(updatecurrentLocation) withObject:nil afterDelay:5];
}
-(void)updatecurrentLocation{
Maplocation.showsUserLocation=YES;
MKCoordinateRegion mapregion;
mapregion.center=Maplocation.userLocation.coordinate;
mapregion.span.latitudeDelta = 2;
mapregion.span.longitudeDelta = 2;
[Maplocation setRegion:mapregion animated: YES];
}
That is it folks :)
I'm trying to alert the user if a web view loads for more than 10 seconds. The error is incase there is slow or no internet connection. The problem is that this code does not work.
In the first case I'm attempting to start the timer when the web view starts loading. Then either expire with doing nothing or display an alert after 10 seconds that the web view is loading very slow or not at all.
In the second case I've attempted to start the timer when the web view starts and either show the alert after 10 seconds or invalidate the timer before it expires.
Neither of which seem to work correctly.
- (void)viewDidLoad
{
timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(webViewLoading) userInfo:nil repeats:YES];
webViewLoadTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(showAlert) userInfo:nil repeats:NO];
[super viewDidLoad];
[self configureView];
}
-(void)showAlert
{
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle: #"Warning"
message: #""
delegate: self
cancelButtonTitle:#"OK"
otherButtonTitles:nil, nil];
if (self.bgWebView.loading){[alert show];}
}
I have tried putting the timer in the following method, but then it alerts every time regardless of whether the device is connected to internet or not. It doesn't seem to invalidate.
-(void)webViewLoading
{
if (self.bgView.loading)
{
[self.avBackGround startAnimating];
webViewLoadTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(showAlert) userInfo:nil repeats:NO];
}
else
{
[self.avBackGround stopAnimating];
[webViewLoadTimer invalidate];
}
if (self.icnWebView.loading)
{
[self.avIcon startAnimating];
}
else
{
[self.avIcon stopAnimating];
}
}
I apologize if I have missed a post on this. I just can't seem to put this together.
Thanks for any help!
I use Matt Gallagher's audio streamer for streaming radio stations. But how to record the audio? Is there a way to get the downloaded packets into NSData and save it in an audio file in the documents folder on the iPhone?
Thanks
Yes, there is and I have done it. My problem is being able to play it back IN the same streamer (asked elsewhere). It will play back with the standard AVAudioPlayer in iOS. However, this will save the data to a file by writing it out in the streamer code.
This example is missing some error checks, but will give you the main idea.
First, a call from the main thread to start and stop recording. This is in my viewController when someone presses record:
//---------------------------------------------------------
// Record button was pressed (toggle on/off)
// writes a file to the documents directory using date and time for the name
//---------------------------------------------------------
-(IBAction)recordButton:(id)sender {
// only start if the streamer is playing (self.streamer is my streamer instance)
if ([self.streamer isPlaying]) {
NSDate *currentDateTime = [NSDate date]; // get current date and time
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateFormatter setDateFormat:#"EEEE MMMM d YYYY 'at' HH:mm:ss"];
NSString *dateString = [dateFormatter stringFromDate:currentDateTime];
self.isRecording = !self.isRecording; // toggle recording state BOOL
if (self.isRecording)
{
// start recording here
// change the record button to show it is recording - this is an IBOutlet
[self.recordButtonImage setImage:[UIImage imageNamed:#"Record2.png"] forState:0];
// call AudioStreamer to start recording. It returns the file pointer back
//
self.recordFilePath = [self.streamer recordStream:TRUE fileName:dateString]; // start file stream and get file pointer
} else
{
//stop recording here
// change the button back
[self.recordButtonImage setImage:[UIImage imageNamed:#"Record.png"] forState:0];
// call streamer code, stop the recording. Also returns the file path again.
self.recordFilePath = [self.streamer recordStream:FALSE fileName:nil]; // stop stream and get file pointer
// add to "recorded files" for selecting a recorderd file later.
// first, add channel, date, time
dateString = [NSString stringWithFormat:#"%# Recorded on %#",self.model.stationName, dateString]; // used to identify the item in a list laster
// the dictionary will be used to hold the data on this recording for display elsewhere
NSDictionary *row1 = [[[NSDictionary alloc] initWithObjectsAndKeys: self.recordFilePath, #"path", dateString, #"dateTime", nil] autorelease];
// save the stream info in an array of recorded Streams
if (self.model.recordedStreamsArray == nil) {
self.model.recordedStreamsArray = [[NSMutableArray alloc] init]// init the array
}
[self.model.recordedStreamsArray addObject:row1]; // dict for this recording
}
}
}
NOW, in AudioStreamer.m I need to handle the record setup call above
- (NSString*)recordStream:(BOOL)record fileName:(NSString *)fileName
{
// this will start/stop recording, and return the file pointer
if (record) {
if (state == AS_PLAYING)
{
// now open a file to save the data into
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
// will call this an mp3 file for now (this may need to change)
NSMutableString *temp = [NSMutableString stringWithString:[documentsDirectory stringByAppendingFormat:#"/%#.mp3",fileName]];
// remove the ':' in the time string, and create a file name w/ time & date
[temp replaceOccurrencesOfString:#":" withString:#"" options:NSLiteralSearch range:NSMakeRange(0, [temp length])];
self.filePath = temp; // file name is date time generated.
NSLog(#"Stream Save File Open = %#", self.filePath);
// open the recording file stream output
self.fileStream = [NSOutputStream outputStreamToFileAtPath:self.filePath append:NO];
[self.fileStream open];
NSLog(#"recording to %#", self.fileStream);
self.isRecording = TRUE;
return (self.filePath); // if started, send back the file path
}
return (nil); // if not started, return nil for error checking
} else {
// save the stream here to a file.
// we are done, close the stream.
if (self.fileStream != nil) {
[self.fileStream close];
self.fileStream = nil;
}
NSLog(#"stop recording");
self.isRecording = FALSE;
return (self.filePath); // when stopping, return nil
}
}
LASTLY, we need to modify the data portion of the streamer to actually save the bytes. You need to modify the stream code in the method: -(void)handleReadFromStream:(CFReadStreamRef)aStreameventType:(CFStreamEventType)eventType
Scroll down in that method until you find:
#synchronized(self)
{
if ([self isFinishing] || !CFReadStreamHasBytesAvailable(stream))
{
return;
}
//
// Read the bytes from the stream
//
length = CFReadStreamRead(stream, bytes, kAQDefaultBufSize);
if (length == -1)
{
[self failWithErrorCode:AS_AUDIO_DATA_NOT_FOUND];
return;
}
RIGHT after the length = line, add the following code:
//
// if recording, save the raw data to a file
//
if(self.isRecording && length != 0){
//
// write the data to a file
//
NSInteger bytesWritten;
NSInteger bytesWrittenSoFar;
bytesWrittenSoFar = 0;
do {
bytesWritten = [self.fileStream write:&bytes[bytesWrittenSoFar] maxLength:length - bytesWrittenSoFar];
NSLog(#"bytesWritten = %i",bytesWritten);
if (bytesWritten == -1) {
[self.fileStream close];
self.fileStream = nil;
NSLog(#"File write error");
break;
} else {
bytesWrittenSoFar += bytesWritten;
}
} while (bytesWrittenSoFar != length);
}
Here are the .h declarations:
Added to the interface for AudioStreamer.h
// for recording and saving a stream
NSString* filePath;
NSOutputStream* fileStream;
BOOL isRecording;
BOOL isPlayingFile;
In your view controller you will need:
#property(nonatomic, assign) IBOutlet UIButton* recordButtonImage;
#property(nonatomic, assign) BOOL isRecording;
#property (nonatomic, copy) NSString* recordFilePath;
Hope this helps someone. Let me know if questions, and always happy to hear someone who can improve this.
Also, someone asked about self.model.xxx Model is a Data Object I created to allow me to easily pass around data that is used by more than one object, and is also modified by more than one object. I know, global data is bad form, but there are times that just make it easier to access. I pass the data model to each new object when called. I save an array of channels, song name, artist name, and other stream related data inside the model. I also put any data I want to persist through launches here, like settings, and write this data model to a file each time a persistent data is changed. IN this example, you can keep the data locally. If you need help on the model passing, let me know.
OK, here is how I play back the recorded file. When playing a file, the station URL contains the path to the file. self.model.playRecordedSong contains a time value for how many seconds into the stream I want to play. I keep a dictionary of song name and time index, so I can jump into the recorded stream at the start of any song. Use 0 to start form the beginning.
NSError *error;
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:self.model.stationURL, [[NSBundle mainBundle] resourcePath]]];
// get the file URL and then create an audio player if we don't already have one.
if (audioPlayer == nil) {
// set the seconds count to the proper start point (0, or some time into the stream)
// this will be 0 for start of stream, or some value passed back if they picked a song.
self.recordPlaySecondsCount = self.model.playRecordedSong;
//create a new player
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
// set self so we can catch the end of file.
[audioPlayer setDelegate: self];
// audio player needs an NSTimeInterval. Get it from the seconds start point.
NSTimeInterval interval = self.model.playRecordedSong;
// seek to the proper place in file.
audioPlayer.currentTime = interval;
}
audioPlayer.numberOfLoops = 0; // do not repeat
if (audioPlayer == nil)
NSLog(#"AVAudiolayer error: %#", error);
// I need to do more on the error of no player
else {
[audioPlayer play];
}
I hope this helps you play back the recorded file.
Try This Class This Have Full Solution OF All Radio streaming recording Playing All..
In Git Hub You Can Find This Use This Class Very Easy To Use