NSTimer never starts - cocoa

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];

Related

PHImagemanager requestImageForAsset memory issue

I want to use PHImagemanager to get all photos on the device.
If I set the targetsize too high the app will crash because of memory warnings. So I tested the request without any use of the returned images and set each image to nil, but still app is crashing. I don't know what I'm doing wrong. Can someone help please?
requestOptions = [[PHImageRequestOptions alloc] init];
requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
requestOptions.synchronous = false;
assetsOfPhotos = [PHAsset fetchAssetsWithMediaType: PHAssetMediaTypeImage options: nil];
PHImageManager *manager = [PHImageManager defaultManager];
#autoreleasepool {
for (int i = 0; i <= totalImages - 1; i++) {
PHAsset *asset = assetsOfPhotos[i];
[manager requestImageForAsset: asset
targetSize: CGSizeMake(640, 480)
contentMode: PHImageContentModeDefault
options: requestOptions
resultHandler: ^void(UIImage *image, NSDictionary *info) {
image = nil;
}];
}
}
Setting size to 640x480 crash after about 200 images, 320x240 after about 800 images. As a 640x480 image needs 4 times memory then 320x240 image it seems that the app crashes after the same amount of memory that was allocated. So for me this means that I cannot show more images than 200 imags with 640x480 on the test device, because I cannot free allocated memory.
In order to make your #autoreleasepool work you need to set requestOptions.synchronous to YES, and use your own async queue if you want to make the request operation asynchronously.
Please use #autoreleasepool inside the for loop.
for (int i = 0; i <= totalImages - 1; i++) {
#autoreleasepool {
//Your code
}
}
If you want load all photos that you have in Photos.app and you didn't want iCloud. You can do:
That example works with a collection view.
#interface GalleryViewModel ()
#property (strong, nonatomic) NSMutableArray<PHAsset *> *assets;
#property (strong, nonatomic) PHImageManager *imageManager;
#property (strong, nonatomic) PHImageRequestOptions *requestOptions;
#property (strong, nonatomic) NSMutableArray<UIImage *> *imagesList;
#end
#implementation GalleryViewModel
- (instancetype) initWithContext:(ITXAppContext *)context {
self = [super initWithContext:context];
if (self) {
_assets = [[NSMutableArray alloc] init];
_imageManager = [PHImageManager defaultManager];
_requestOptions = [[PHImageRequestOptions alloc] init];
_imagesList = [[NSMutableArray alloc] init];
}
return self;
}
#pragma mark - Public methods
// ==================================================================================
// Public methods
- (void) viewModelDidLoad {
[self obtainAllPhotos];
}
#pragma mark - Private methods
// ==================================================================================
// Private methods
- (void) obtainAllPhotos {
self.requestOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
self.requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
self.requestOptions.synchronous = YES;
self.requestOptions.networkAccessAllowed = NO;
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
fetchOptions.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:NO]];
PHFetchResult<PHAsset *> *result = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOptions];
__weak GalleryViewModel *weakSelf = self;
[result enumerateObjectsUsingBlock:^(PHAsset * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[weakSelf.assets addObject:obj];
if (idx >= ([result count] - 1)) {
[weakSelf.viewDelegate setupView];
}
}];
}
#pragma mark - Get data from object
// ==================================================================================
// Get data from object
- (NSInteger) sizeGallery {
if (self.assets) {
return [self.assets count];
}
return 0;
}
- (UIImage *) imagesFromList:(NSInteger) index {
__block UIImage *imageBlock;
[self.imageManager requestImageForAsset:[self.assets objectAtIndex:index] targetSize:CGSizeMake(200, 200) contentMode:PHImageContentModeAspectFit options:self.requestOptions resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
if (result) {
imageBlock = result;
}
}];
return imageBlock;
}
#end

GCDasyncUdpSocket can't reveive packets after changing from broadcast to unicast mode

Developing an iPAD app which communicates via WiFi to a UDP to serial converter. App starts out in broadcast mode and retrieves a list of responding units (requestPodIds). The received data maps a SN to the units IP address. The selected IP address is then used to communicate point-to-point between the iPad and the UDP/serial converter.
My code works fine on the broadcast communications. And the unicast message I send out (requestStatus) to the units IP address is being received by the converter and it is responding as expected. However, I do not get any data back into the didReceiveData method.
I do not understand why I am not getting the data back in the method:
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
fromAddress:(NSData *)address
withFilterContext:(id)filterContext
My socket connections are located in the AppDelegate and the calls are mode from a viewController.
Buttons on viewController are pressed as follows:
1. button1Click
2. button2Click; this calls requestPodIds; works fine; data returned which fills ten other button labels with pod ids; one is clicked;
3. getPodIdsClick; this loads the IP address of the UDP/Serial converter;
4. getStatusClick; this is where the problem occurs. The UDP message goes out, the UDP converter receives the message and responds, I never see the response data in didReceiveData.
AppDelegate code:
- (int) udpServiceStart {
NSError * UDPError;
if(GCDUdpSocket == nil)
{
GCDUdpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
}
GCDUdpSocket.delegate = self;
[GCDUdpSocket setIPv4Enabled:YES];
[GCDUdpSocket setIPv6Enabled:NO];
[GCDUdpSocket enableBroadcast:YES error:&UDPError];
if (![GCDUdpSocket bindToPort:udpPort error:&UDPError]) { NSLog(#"Error starting server (bind): %#", UDPError);
return -1;
}
if (![GCDUdpSocket beginReceiving:&UDPError])
// if (![GCDUdpSocket receiveOnce:&UDPError])
{
[GCDUdpSocket close];
NSLog(#"Error starting server (recv): %#", UDPError);
return -1;
}
NSLog(#"UDP Link started on port %hu", [GCDUdpSocket localPort]);
return 0;
}
- (void) connectToPodAtAddress: (NSString *) ipAddress
{
NSError * UDPError;
[GCDUdpSocket enableBroadcast:NO error:&UDPError];
podIp = ipAddress;
// if (![GCDUdpSocket connectToHost:podIp onPort:udpPort error:&UDPError])
// {
// [GCDUdpSocket close];
// NSLog(#"Error connecting to host: %#", UDPError);
// return;
// }
// if (![GCDUdpSocket beginReceiving:&UDPError])
// // if (![GCDUdpSocket receiveOnce:&UDPError])
// {
// [GCDUdpSocket close];
// NSLog(#"Error starting server (recv): %#", UDPError);
// return;
// }
}
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
fromAddress:(NSData *)address
withFilterContext:(id)filterContext
{
// NSError * UDPError;
NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (msg)
{
NSLog(#"RCV: %#", msg);
if(!commandBuffer) commandBuffer = [[NSMutableString alloc] initWithString:#""];
// Append the current message portion to the total message
[commandBuffer appendString:msg];
NSString * commands = [[NSString alloc] initWithString: commandBuffer];
NSInteger cr_index = [commands rangeOfString:#"\r"].location;
if([commands rangeOfString:#"\r"].location == NSNotFound)
{
if([commands rangeOfString:#"~~~ds"].location != NSNotFound)
{
[commandBuffer setString:#""];
}
} else {
[self decodeMessage:commands];
}
}
else
{
NSString *host = nil;
uint16_t thePort = 0;
[GCDAsyncUdpSocket getHost:&host port:&thePort fromAddress:address];
NSLog(#"Unknown message from : %#:%hu", host, thePort);
}
//
// Queue up to Read Next Message
//
// if (![GCDUdpSocket receiveOnce:&UDPError])
// {
// [GCDUdpSocket close];
// NSLog(#"Error starting server (recv): %#", UDPError);
// return;
// }
}
- (void)udpSendToHost:(NSString *)host onPort:(int) thePort theMessage: (NSString *) msg
{
NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];
tag = 0;
[GCDUdpSocket sendData:data toHost:host port:thePort withTimeout:-1 tag:tag];
NSLog(#"SENT message for tag (%i) to host %#:%i", (int)tag, host, thePort);
NSLog(#"Message sent = (%#)", msg);
}
- (void) requestPodIds
{
#ifdef LOGFUNCTIONCALLS
NSLog(logString,__FUNCTION__);
#endif
[podTable removeAllObjects]; // pod_table.Clear(); // Empty table of any previous responses
if (GCDUdpSocket != nil) // null happens on startup if wireless adapter not found
{
// PodMessage to_pod = new PodMessage(PodCommands.ucget, PodParameters.serial_number);
PodMessage *to_pod = [[PodMessage alloc] initWithCommand:ucget andParams:serial_number];
// int bytes = udp_socket.SendTo(Encoding.ASCII.GetBytes(to_pod.Message()),
// new IPEndPoint(IPAddress.Broadcast, port));
[self udpSendToHost:podIp onPort:udpPort theMessage:to_pod.message];
// Debug.Assert(bytes == to_pod.Message().Length);
}
}
- (void) requestStatus
{
if (GCDUdpSocket != nil) // null happens on startup if wireless adapter not found
{
// PodMessage to_pod = new PodMessage(PodCommands.ucget, PodParameters.serial_number);
PodMessage *to_pod1 = [[PodMessage alloc] initWithCommand:ucget andParams:status_pod];
[self udpSendToHost:podIp onPort:udpPort theMessage:to_pod1.message];
// PodMessage *to_pod2 = [[PodMessage alloc] initWithCommand:ucget andParams:status_line0];
// [self udpSendToHost:podIp onPort:udpPort theMessage:to_pod2.message];
// PodMessage *to_pod3 = [[PodMessage alloc] initWithCommand:ucget andParams:status_line1];
// [self udpSendToHost:podIp onPort:udpPort theMessage:to_pod3.message];
// Debug.Assert(bytes == to_pod.Message().Length);
}
}
Access from the ViewController is as follows:
wifiTestAppDelegate *appDelegate;
appDelegate = (wifiTestAppDelegate *)[[UIApplication sharedApplication] delegate];
- (IBAction)button1Click:(id)sender {
[appDelegate udpServiceStart];
}
- (IBAction)button2Click:(id)sender {
if([GCDUdpSocket isClosed])
{
NSLog(#"Socket is Closed!");
} else {
if([GCDUdpSocket isConnected])
{
NSLog(#"Socket is Connected! Can't Broadcast!");
} else {
[appDelegate requestPodIds];
}
}
}
- (IBAction)podSelectClick:(id)sender {
NSLog(#"Button with label %#",((UIButton *)sender).titleLabel.text);
NSLog(#"Button with tag %i",((UIButton *)sender).tag);
// UIButton *button = (UIButton *)[self.view viewWithTag:buttonTag++];
// [button setTitle:[podTable objectForKey:key] forState:UIControlStateNormal];
NSString * ipAddress = [podTable objectForKey:((UIButton *)sender).titleLabel.text];
NSLog(#"IP Address = %#",ipAddress);
podIp = ipAddress;
pod_sn = ((UIButton *)sender).titleLabel.text;
[appDelegate connectToPodAtAddress:ipAddress];
getStatusButton.hidden = NO;
[selectPodButton setTitle:ipAddress forState:UIControlStateNormal];
}
- (IBAction)getPodIdsClick:(id)sender {
int buttonTag = 101;
NSMutableArray * sortArray = [[NSMutableArray alloc] initWithCapacity:100];
if([podTable count] > 0)
{
for (NSString *key in podTable)
{
[sortArray addObject:key];
}
[sortArray sortUsingSelector:#selector(compare:)];
for(int i=0; i < [podTable count]; i++)
{
UIButton *button = (UIButton *)[self.view viewWithTag:buttonTag++];
[button setTitle:[sortArray objectAtIndex:i] forState:UIControlStateNormal];
}
}
}
- (IBAction)getStatusClick:(id)sender {
[appDelegate requestStatus];
}
No luck in getting GCDAsyncUdpSocket to send other than class broadcast. Resolved with implementation of BSD sockets solution for broadcast portion of code:
int fd;
int err;
int junk;
struct sockaddr_in addr;
NSData * data;
ssize_t bytesSent;
static const int kOne = 1;
fd = socket(AF_INET, SOCK_DGRAM, 0);
assert(fd >= 0);
err = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &kOne, sizeof(kOne));
assert(err == 0);
data = [#"hello" dataUsingEncoding:NSUTF8StringEncoding];
assert(data != nil);
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_len = sizeof(addr);
addr.sin_port = htons(8023);
addr.sin_addr.s_addr = htonl(0xffffffff); // 255.255.255.255
bytesSent = sendto(fd, [data bytes], [data length], 0, (const struct sockaddr *) &addr, sizeof(addr));
NSLog(#"bytes sent = %zd", bytesSent);
junk = close(fd);
assert(junk == 0);

Issue in writing `NSOutputStream`.

I have one basic question,
While working with NSOutputStream, should we wait for NSStreamEventHasSpaceAvailable to send the packet, so we can call , [NSOutputStream write] as and when its needed,
I believe NSStream should take care of write function...
if this is not correct, then please provide your views on following logic,
===== To Write on NSOutputStream =================
Have Queue to add packet, that to be sent
// StreamQueue.h
#interface StreamQueue : NSObject <NSCoding>
{
NSMutableArray * data;
NSRecursiveLock * theLock;
}
#pragma mark �Initialization & Deallocation�
- (id)init;
- (id)initWithQueue:(CommQueue *)queue;
- (id)initWithCoder:(NSCoder *)coder;
- (void)dealloc;
- (void)encodeWithCoder:(NSCoder *)coder;
#pragma mark
#pragma mark �Accessor Methods�
- (int)size;
- (BOOL)isEmpty;
- (id)top;
- (NSArray *)data;
#pragma mark
#pragma mark �Modifier Methods�
- (void)enqueue:(id)object;
- (id)dequeue;
- (void)removeAll;
#end
and its implementation
#import "StreamQueue.h"
#implementation StreamQueue
#pragma mark �Initialization & Deallocation�
- (id)init
{
if (self = [super init]) {
data = [[NSMutableArray alloc] init];
theLock = [[NSRecursiveLock alloc] init];
}
return self;
}
- (id)initWithQueue:(StreamQueue *)queue
{
if (self = [super init]) {
data = [[NSMutableArray alloc] initWithArray:[queue data]];
theLock = [[NSRecursiveLock alloc] init];
}
return self;
}
- (id)initWithCoder:(NSCoder *)coder
{
if (self = [super init]) {
data = [[NSMutableArray alloc] initWithArray:[coder decodeObject]];
theLock = [[NSRecursiveLock alloc] init];
}
return self;
}
- (void)dealloc
{
[data release];
[theLock release];
[super dealloc];
}
- (void)encodeWithCoder:(NSCoder *)coder;
{
[coder encodeObject:data];
}
#pragma mark
#pragma mark �Accessor Methods�
- (int)size
{
int size;
[theLock lock];
size = [data count];
[theLock unlock];
return size;
}
- (BOOL)isEmpty
{
BOOL empty;
[theLock lock];
empty = ([data count] == 0);
[theLock unlock];
return empty;
}
- (id)top
{
id object = nil;
[theLock lock];
if (![self isEmpty])
object = [data objectAtIndex:0];
[theLock unlock];
return object;
}
- (NSArray *)data
{
NSArray * array;
[theLock lock];
array = [NSArray arrayWithArray:data];
[theLock unlock];
return array;
}
#pragma mark
#pragma mark �Modifier Methods�
- (void)enqueue:(id)object
{
[theLock lock];
[data addObject:object];
[theLock unlock];
}
- (id)dequeue
{
id object = [self top];
if (object != nil) {
[theLock lock];
[object retain];
[data removeObjectAtIndex:0];
[theLock unlock];
}
return [object autorelease];
}
- (void)removeAll
{
[theLock lock];
while (![self isEmpty])
[data removeObjectAtIndex:0];
[theLock unlock];
}
#end
Now when Application have something to send over socket(NSStream), it should add it into the queue,
-(bool)sendRawData:(const uint8_t *)data length:(int)len{
// if still negotiating then don't send data
assert(!networkConnected);
NSData *pData = [NSData dataWithBytes:(const void *)data length:len];
// pToSendPacket is of type StreamQueue
[pToSendPacket enqueue:pData];
return;
}
and this piece of code when we get NSHasSpaceAvailableEvent
-(void)gotSpaceAvailable{
// is there any pending packets that to be send.
NSData *pData = (NSData *)[pToSendPacket dequeue];
if(pData == nil){
// no pending packets..
return;
}
const uint8_t *data = (const uint8_t *)[pData bytes];
int len = [pData length];
int sendlength = [pOutputStream write:data maxLength:len];
if(sendlength == -1 ){
NSError *theError = [pOutputStream streamError];
NSString *pString = [theError localizedDescription];
int errorCode = [theError code];
return ;
}
}
I was expecting Application will keep on receiving the event, whenever OutputStream sends data, but i recieved only once... :(
Please help ...
If you don't wait for the event, the write call will block until space is available. Generally you want to aim to design your code to work asychronously, so waiting for NSStreamEventHasSpaceAvailable is the best solution.
As for when you receive the space available notification, see the documentation here:
If the delegate receives an NSStreamEventHasSpaceAvailable event and
does not write anything to the stream, it does not receive further
space-available events from the run loop until the NSOutputStream
object receives more bytes. When this happens, the run loop is
restarted for space-available events. If this scenario is likely in
your implementation, you can have the delegate set a flag when it
doesn’t write to the stream upon receiving an
NSStreamEventHasSpaceAvailable event. Later, when your program has
more bytes to write, it can check this flag and, if set, write to the
output-stream instance directly.
There is no firm guideline on how many bytes to write at one time.
Although it may be possible to write all the data to the stream in one
event, this depends on external factors, such as the behavior of the
kernel and device and socket characteristics. The best approach is to
use some reasonable buffer size, such as 512 bytes, one kilobyte (as
in the example above), or a page size (four kilobytes).
So you should be getting regular NSStreamEventHasSpaceAvailable events as long as you do write data for each event.

NSFileHandle function acceptConnectionInBackgroundAndNotify not working when called a second time

I'm using cocoa and i'm trying to create a tcp socket server for a program i'm writing, i'm using NSFileHandle and the method acceptConnectionInBackgroundAndNotify. I've been following the code from here.
The problem i'm having is that I can connect my client once but when I try to connect again I get a connection refused error.
Before I connect for the first time the command sudo lsof -i -P returns the following information:
RubyBeat 23964 pauljohnson 8u IPv4 0x0632c274 0t0 TCP *:1234 (LISTEN)
After connecting the first time with the client I get this:
RubyBeat 23964 pauljohnson 5u IPv4 0x06a30334 0t0 TCP localhost:1234->localhost:51579 (CLOSED)
My application doesn't seem to be reopening the socket to listen for connections after the first one comes in and gets a error number 22 when I try to connect a second time.
I'm using the garbage collection in cocoa and was wondering if that would cause problems with sockets?
My code is:
[self createSocket];
}
-(void)createSocket
{
// create socket and wait for events to come in
NSSocketPort* serverSock = [[NSSocketPort alloc] initWithTCPPort: 1234];
socketHandle = [[NSFileHandle alloc] initWithFileDescriptor: [serverSock socket]
closeOnDealloc: NO];
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(newConnection:)
name: NSFileHandleConnectionAcceptedNotification
object: socketHandle];
[socketHandle acceptConnectionInBackgroundAndNotify];
}
- (void)newConnection:(NSNotification*)notification
{
NSLog(#"connection accepted");
NSDictionary* userInfo = [notification userInfo];
NSFileHandle* remoteFileHandle = [userInfo objectForKey:
NSFileHandleNotificationFileHandleItem];
if([[userInfo allKeys] containsObject:#"NSFileHandleError"]){
NSNumber* errorNo = [userInfo objectForKey:#"NSFileHandleError"];
if( errorNo ) {
NSLog(#"NSFileHandle Error: %#", errorNo);
return;
}
}
[socketHandle acceptConnectionInBackgroundAndNotify];
[[NSNotificationCenter defaultCenter] addObserver:self
selector: #selector(processSocketData:)
name: NSFileHandleReadCompletionNotification
object: remoteFileHandle];
// Send a message to the client, acknowledging that the connection was accepted
[remoteFileHandle writeData: [#"OK" dataUsingEncoding: NSASCIIStringEncoding]];
[remoteFileHandle readInBackgroundAndNotify];
}
/*
Handle client data
*/
- (void)processSocketData:(NSNotification *)note
{
NSData *data = [[note userInfo]
objectForKey:NSFileHandleNotificationDataItem];
NSNumber* errorNo = [[note userInfo] objectForKey:#"NSFileHandleError"];
if( errorNo ) {
NSLog(#"NSFileHandle Error: %#", errorNo);
return;
}
// Do something here with your data
// search string for \n\n that terminates event
NSString* stringData = [[NSString alloc] initWithData: data encoding: NSASCIIStringEncoding];
NSLog(#"data received: %#", stringData);
NSDictionary* lastEvent = nil;
NSRange range = [stringData rangeOfString: #"\n\n"];
if (range.location != NSNotFound) {
NSArray* subStrings = [stringData componentsSeparatedByString:#"\n\n"];
NSMutableArray* events = [[NSMutableArray alloc] init];
[eventBuffer appendString: [subStrings objectAtIndex: 0]];
// handle first event - could be in parts
NSDictionary * json = (NSDictionary*)[eventBuffer JSONValue];
[events addObject:json];
for(int i = 1; i < [subStrings count]-1; i++){
NSString* subString = [subStrings indexOfObject:i];
NSDictionary * eventJson = (NSDictionary*)[subString JSONValue];
[events addObject:eventJson];
}
// we have at least one event to draw here
for(NSDictionary* event in events){
NSLog(#"event: %#", [event objectForKey:#"type"]);
}
lastEvent = [events lastObject];
// clear eventBuffer
[eventBuffer setString:#""];
// add end of data to eventBuffer?
}else {
[eventBuffer appendString:stringData];
}
// check event if it is a program exit event then stop receiving data
if([[lastEvent objectForKey: #"type"] compare: #"exit"] != NSOrderedSame){
// Tell file handle to continue waiting for data
[[note object] readInBackgroundAndNotify];
}else {
NSLog(#"exit received stopping receiving data");
}
}
I have met this problem before.
Once all data is read, do another "accept connection in background" for another round of reading data.

NSTimer within NSThread Memory grows?

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.

Resources