I am using a timer within a thread , i am instantiating the timer all the time however when i run the application within instrument i see the memory grows exponentially, i am not having any leaks. I am sure that NSThread is causing the growth ? . I am stopping the current timer before running a new one (singleton) and i am releasing everything in my stopTimer, but not sure why the memory still grow?
- (void)startingTimer{
[view addSubview:timerBackground];
timerThread = [[NSThread alloc] initWithTarget:timer selector:#selector(startTimerThread) object:nil]; //Create a new thread
[timerThread start]; //start the thread
}
//the thread starts by sending this message
- (void) startTimerThread{
NSAutoreleasePool * timerNSPool = [[NSAutoreleasePool alloc] init];
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
isTimerRunning = YES;
nsTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(startTime:) userInfo:nil repeats:YES];
[runLoop run];
[timerNSPool release];
}
- (void)startTime:(NSTimer *)theTimer{
if(timeDuration > 1){
--timeDuration;
//[self updateTextLbl];
[timer performSelectorOnMainThread:#selector(updateTextLbl) withObject:nil waitUntilDone:YES];
}else{
[timer stopTimer];
if(delegate)
[delegate timeIsUp];
}
}
- (void) stopTimer{
isTimerRunning = NO;
[nsTimer invalidate];
[timerThread release];
nsTimer = nil;
timerBackground.alpha = 0.0;
timerBackground.frame = CGRectMake(TIMER2_BG_X - TIMER2_BG_WIDTH, TIMER2_BG_Y, TIMER2_BG_WIDTH, TIMER2_BG_HEIGHT);
}
- (void)updateTextLbl{
timeLabel.text = [NSString stringWithFormat:#"%d",timeDuration];
}
timerThread is released correctly however you never release nsTimer in stopTimer. I assume you store it in a property with retain specified and then you will need to release it.
Related
I want to use a little animation for my labels.
If I try to call this animation from a IBAction button or ViewDidLoad and all work correctly.
- (void)animateLabelShowText:(NSString*)newText characterDelay:(NSTimeInterval)delay
{
[self.myLabel setText:#""];
for (int i=0; i<newText.length; i++)
{
dispatch_async(dispatch_get_main_queue(),
^{
[self.myLabel setText:[NSString stringWithFormat:#"%#%C", self.myLabel.text, [newText characterAtIndex:i]]];
});
[NSThread sleepForTimeInterval:delay];
}
}
called by:
-(void) doStuff
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
^{
[self animateLabelShowText:#"Hello Vignesh Kumar!" characterDelay:0.5];
});
}
But if I put this method in a protocol and I try to call it from for example a delegate nothing appears. There's probably I missing something in the GDC (Grand Central Dispatch) logics:
...
if ([_myDelegate respondsToSelector:#selector(doStuff:)])
{
NSLog(#" Yes I'm in and try to execute doStuff..");
[_myDelegate doStuff]; // NOTHING TO DO
}
...
The same situation happened when I change my function doStuff with any similar function like:
-(void)typingLabel:(NSTimer*)theTimer
{
NSString *theString = [theTimer.userInfo objectForKey:#"string"];
int currentCount = [[theTimer.userInfo objectForKey:#"currentCount"] intValue];
currentCount ++;
NSLog(#"%#", [theString substringToIndex:currentCount]);
[theTimer.userInfo setObject:[NSNumber numberWithInt:currentCount] forKey:#"currentCount"];
if (currentCount > theString.length-1) {
[theTimer invalidate];
}
[self.myLabel setText:[theString substringToIndex:currentCount]];
}
called by
-(void) doStuff
{
NSString *string =#"Risa Kasumi & Yuma Asami";
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:string forKey:#"string"];
[dict setObject:#0 forKey:#"currentCount"];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(typingLabel:) userInfo:dict repeats:YES];
[timer fire];
}
I've solved using NSNotificationCenter in my delegate file .m
in viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(makeEffectOnMyLabel:) name:#"lblUpdate" object:nil];
in my protocol function :
dispatch_async( dispatch_get_main_queue(),
^{
...I've populated a dictionary userInfo with my vars
[[NSNotificationCenter defaultCenter] postNotificationName:#"lblUpdate" object:nil userInfo:userInfo];
});
in a function created to prepare typingLabel
-(void) makeEffectOnMyLabel:(NSNotification *) notification
{
..changes between dictionaries to prepare typingLabel..
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
NSDictionary* userInfo = [notification userInfo];
[dict setObject:[userInfo objectForKey:#"myRes"] forKey:#"myRes"];
[dict setObject:[userInfo objectForKey:#"myType"] forKey:#"myType"];
[dict setObject:[userInfo objectForKey:#"frase"] forKey:#"frase"];
[dict setObject:#0 forKey:#"currentCount"];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(typingLabel:) userInfo:dict repeats:YES];
[timer fire];
}
Now all work correct.
When I use the following code I was told there is a leak:
- (void)dealloc
{
[connection release], connection = nil;
[responseData release],responseData = nil;
[cityCode release], cityCode = nil;
[requestUrlString release], requestUrlString = nil;
[returnDataDic release], returnDataDic = nil;
[super dealloc];
}
- (id)initWithCityCode:(NSString *)aCityCode
requestURL:(NSString*)urlString
responseType:(SWEngineRequestType)theResponsetype
target:(id)theTarget
action:(SEL)theAction
{
if ((self = [super init]))
{
_isExecuting = NO;
_isFinished = NO;
target = theTarget;
action = theAction;
cityCode = [aCityCode retain];
requestUrlString = [urlString copy];
responseType = theResponsetype;
returnDataDic = [[NSMutableDictionary alloc] initWithCapacity:1];
if (cityCode)
{
[returnDataDic setObject:cityCode forKey:SWEATHER_CITYCODE];
}
[returnDataDic setObject:[NSNumber numberWithInt:responseType] forKey:SWEATHER_DOWNTYPE];
}
return self;
}
- (BOOL)isConcurrent
{
return YES;
}
- (void)finish
{
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:#"isExecuting"];
[self didChangeValueForKey:#"isFinished"];
[connection release], connection = nil;
[responseData release],responseData = nil;
[cityCode release], cityCode = nil;
[requestUrlString release], requestUrlString = nil;
[returnDataDic release], returnDataDic = nil;
done = YES;
}
- (BOOL)isExecuting
{
return _isExecuting;
}
- (BOOL)isFinished
{
return _isFinished;
}
- (void)main
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
done = NO;
if ([self isCancelled])
{
[self willChangeValueForKey:#"isFinished"];
_isFinished = YES;
[self didChangeValueForKey:#"isFinished"];
[pool release];
return;
}
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:#"isExecuting"];
NSURL * urlToDownLoad = [NSURL URLWithString:[requestUrlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURLRequest *request = [NSURLRequest requestWithURL:urlToDownLoad cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:20];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (connection)
{
responseData = [[NSMutableData alloc] init];
[connection start];
}
else
{
[self finish];
}
if (connection != nil)
{
do
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
while (!done);
}
[pool release], pool = nil;
}
#pragma mark -
#pragma mark - NSURLConnectionDataDelegate methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[returnDataDic setObject:#"error" forKey:...];
[target performSelectorOnMainThread:action withObject:returnDataDic waitUntilDone:NO];
[self finish];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[returnDataDic setObject:responseData forKey:...];
[target performSelectorOnMainThread:action withObject:returnDataDic waitUntilDone:NO];
[self finish];
}
#end
the instrument gave me a leak at: [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; Why? Thanks!
I just want to have a Asynchronous download at a operation but I use the NSAutoreleasePool then the instrument gave a leak at the:[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];.
Try putting your
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
in an autorelease pool.
What object did Instruments identify as having leaked? Where was it allocated? What was the stack trace?
When you run a run loop, all of the run loop sources and run loop observers may fire. So those few lines of code hide a near infinite set of possible things happening as the run loop runs. Any one of those may have a leak, or Instruments may be mistaken.
It is usually a bad idea to run the run loop in the default mode in an inner loop. It's not clear what you're trying to do with that loop, but generally you should schedule any of your own run loop sources in a private run loop mode and then run the run loop in that mode. That way, you're sure that only your own sources get run, which is usually what you want.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
NSTimer doesn't stop
I am having a hard time stopping my timer, witch pings to my server.
I already searched for other answers here and on other places, but i can`t seem to find where i have gone wrong.
I decided to make an example code with the same idea, but, you click a button the timer starts, you click another the timer ends, and it worked the way it should. Please don't mind if i did something wrong (other than the timer part) i'm new in this. All i want to know is why won`t it stop..
Thanks in advance.
Connection.h
#import <Foundation/Foundation.h>
#interface Connection : NSObject
{
NSString *urlString;
NSURL *url;
NSMutableURLRequest *request;
NSURLConnection *connection;
NSURLResponse *response;
NSMutableData *receivedData;
NSData *responseData;
NSError *error;
NSTimer *timer;
}
#property (nonatomic, retain) NSTimer *timer;
-(BOOL)authenticateUser:(NSString *)userName Password:(NSString *)password;
-(BOOL)checkConnection;
-(void)ping:(NSTimer *)aTimer;
-(void)logout;
-(void)timerStart;
-(void)timerStop;
#end
Connection.m
#import "Connection.h"
#import "Parser.h"
#import "Reachability.h"
#import "TBXML.h"
#implementation Connection
#synthesize timer;
-(BOOL) authenticateUser:(NSString *)userName Password:(NSString *)password
{
BOOL success;
urlString = [[NSString alloc] initWithFormat:#"my/server/address/login"];
url =[[NSURL alloc] initWithString:urlString];
request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10];
error = [[NSError alloc] init];
responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
[responseData retain];
NSString *tempString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSMutableDictionary *tempDict= [[NSMutableDictionary alloc] init];
if (request)
{
Parser *parser = [[Parser alloc] init];
tempDict = [parser readXMLString:tempString];
for (id key in tempDict)
{
NSLog(#"%# is %#",key,[tempDict objectForKey:key]);
}
if ([[tempDict objectForKey:#"login"] isEqualToString:#"true"] )
{
success = YES;
self.timerStart;
}
else
{
success = NO;
}
}
[urlString release];
[url release];
[error release];
[responseData release];
[tempString release];
return success;
}
-(void)logout
{
self.timerStop;
}
-(void)ping:(NSTimer *)aTimer;
{
urlString = [[NSString alloc] initWithFormat:#"my/server/address"];
url =[[NSURL alloc] initWithString:urlString];
request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10];
NSLog(#"ping");
[urlString release];
[url release];
}
-(BOOL)checkConnection
{
Reachability *reachability = [Reachability reachabilityWithHostName:#"http://my/server/address"];
NetworkStatus internetStatus = [reachability currentReachabilityStatus];
if ((internetStatus != ReachableViaWiFi) && (internetStatus != ReachableViaWWAN))
{
return NO;
}
else
{
return YES;
}
}
-(void)timerStart
{
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(ping:) userInfo:nil repeats:YES];
}
-(void)timerStop
{
[self.timer invalidate];
self.timer = nil;
}
#end
In timerStart you just replace whatever is in the timer property. If you start a second timer without stopping the first one, it will run forever. So timerStart should first call timerStop before creating a new one (and should probably have a new name then as it would be silly to call timerStop from timerStart).
Use [self timerStop]; using dot syntax is ONLY for properties (and will generate a warning if you don't), not calling a method in the way you're doing it.
Edit: This won't fix your problem, but doing it the way you are is very bad coding practice
-(void)timerStart
{
[self.timer invalidate];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(ping:) userInfo:nil repeats:YES];
}
I am using property. self.refreshTimer = nil; In this string I got CFRelease error.
Why do I get an error?
#property (nonatomic, retain) NSTimer* refreshTimer;
- (id) init
{
self = [super init];
if (self != nil)
{
self.refreshTimer = [NSTimer timerWithTimeInterval:600 target:self selector:#selector(timerRefreshGPS:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:refreshTimer forMode:NSDefaultRunLoopMode];
}
return self;
}
-(void) updateUserGPSLocation:(CLLocation*)newLocation
{
[refreshTimer invalidate];
[refreshTimer release];
self.refreshTimer = nil;
self.refreshTimer = [NSTimer timerWithTimeInterval:600 target:self selector:#selector(timerRefreshGPS:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:refreshTimer forMode:NSDefaultRunLoopMode];
}
- (void)dealloc
{
[refreshTimer invalidate];
[refreshTimer release];
self.refreshTimer = nil;
[super dealloc];
}
self.refreshTimer = nil;
setting self.refreshTimer = nil, since refreshTimer is a property has the effect of releasing the current value of refreshTimer, then assigning nil to it. You've double-released.
I'm also not certain that you have the references that you think you have there - the form of the allocation you use should return an autoreleased timer, and it will be retained when you install it in the run loop. I don't think you actually own a reference here.
I'm just trying to close an NSPanel after a couple second delay, but I can't get my NSTimer to start. It will fire if I explicitly call the fire method on it, but it will never go by itself. Here's my code:
- (void)startRemoveProgressTimer:(NSNotification *)notification {
NSLog(#"timer should start");
timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(removeProgress:) userInfo:nil repeats:NO];
}
- (void)removeProgress:(NSTimer *)timer {
[progressPanel close];
}
I do have some threading in my code as such. I assume this is what's messing my timer up.
-(void)incomingTextUpdateThread:(NSThread*)parentThread {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//mark the thread as running
readThreadRunning = TRUE;
const int BUFFER_SIZE = 100;
char byte_buffer[BUFFER_SIZE]; //buffer for holding incoming data
int numBytes = 0; //number of bytes read
NSString *text; //incoming text from the serial port
[NSThread setThreadPriority:1.0];
//this will loop until the serial port closes
while (TRUE) {
//read() blocks until some data is available or the port is closed
numBytes = read(serialFileDescriptor, byte_buffer, BUFFER_SIZE);
if(numBytes > 0) {
//creat a string from the incoming bytes
text = [[[NSString alloc] initWithBytes:byte_buffer length:numBytes encoding:[NSString defaultCStringEncoding]] autorelease];
if(!([text rangeOfString:SEND_NEXT_COORDINATE].location == NSNotFound)) {
//look for <next> to see if the next data should be sent
if(coordinateNum <[coordinatesArray count]) {
[self sendNextCoordinate]; //send coordinates
}
else {
[self writeString:FINISH_COORDINATES_TRANSMIT]; //send <end> to mark transmission as complete
NSNumber *total = [NSNumber numberWithUnsignedInteger:[coordinatesArray count]];
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:total forKey:#"progress"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"uploadProgressChange" object:self userInfo:userInfo]; //update progress bar to completed
}
}
[self performSelectorOnMainThread:#selector(appendToIncomingText:) withObject:text waitUntilDone:YES]; //write incoming text to NSTextView
} else {
break; //Stop the thread if there is an error
}
}
// make sure the serial port is closed
if (serialFileDescriptor != -1) {
close(serialFileDescriptor);
serialFileDescriptor = -1;
}
// mark that the thread has quit
readThreadRunning = FALSE;
// give back the pool
[pool release];
}
Which is called from another method by: [self performSelectorInBackground:#selector(incomingTextUpdateThread:) withObject:[NSThread currentThread]];
Thank you rgeorge!!
Adding the timer to the run loop manually made it work!
timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:#selector(removeProgress:) userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];