EXC_Bad_Access When Calling Method from Class - iphone

All,
I have a view controller (let's call it testViewController) that calls a method in another class (class name scan, method name initNetworkCommunication).
Here is the view controller's button method:
-(IBAction) test
{
Scan *canConnect = [[Scan alloc] init];
[canConnect initNetworkCommunication];
}
And here is the class
//scan.h
#interface Scan : NSObject <NSStreamDelegate>
{
NSInputStream *inputStream;
NSOutputStream *outputStream;
}
-(void) scan;
-(void) initNetworkCommunication;
#property (nonatomic, retain) NSInputStream *inputStream;
#property (nonatomic, retain) NSOutputStream *outputStream;
#end
//scan.m
#import "Scan.h"
#implementation Scan
#synthesize inputStream, outputStream;
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
-(void) initNetworkCommunication
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"192.168.17.1", 2004, &readStream, &writeStream);
NSLog(#"readStream %#", readStream);
NSLog(#"writeStream %#", writeStream);
inputStream = (NSInputStream *) readStream;
outputStream = (NSOutputStream *) writeStream;//this __strong may work!
NSLog(#"inputStream %#", inputStream);
NSLog(#"outputStream %#", outputStream);
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
NSLog(#"She be opened, sir!");
return;
}
//more code is here, dealloc, etc
I'm getting the EXC_BAD_ACCESS, and when I enable NSZombieEnable, my debugging session looks like this:
2011-07-15 13:09:46.210 Project[1176:f203] readStream <__NSCFInputStream: 0x6e7a2a0>
2011-07-15 13:09:46.212 Project[1176:f203] writeStream <__NSCFOutputStream: 0x6e7a340>
2011-07-15 13:09:46.213 Project[1176:f203] inputStream <__NSCFInputStream: 0x6e7a2a0>
2011-07-15 13:09:46.214 Project[1176:f203] outputStream <__NSCFOutputStream: 0x6e7a340>
2011-07-15 13:09:46.215 Project[1176:f203] She be opened, sir!
2011-07-15 13:09:46.220 Project[1176:f203] *** -[Scan respondsToSelector:]: message sent to deallocated instance 0x6e79b20
Current language: auto; currently objective-c
(gdb)
It crashes at the three asterisks, and that's my zombie message.
What should I do?
EDIT:After running the program in Instruments, I see no memory leaks. The thing that's getting me is showing in Instruments as SocketStream::dispatchSignalFromSocketCallbackUnlocked(SocketStream)

You have forgotten to retain the Scan object that is the stream's delegate.
Until the streams are closed, your stream delegate will receive messages, and if the delegate gets deallocated, then your app will crash.
In your example code (where you're leaking the canConnect object) it should be fine, but in your real code you might be releasing the Scan object too soon.

change inputStream , outputStream to with self with dot syntax everywhere
like this everywhere
because if u synthesize the property u have to use self with dot syntax to retain them .
self.inputStream = (NSInputStream *) readStream;
self.outputStream = (NSOutputStream *) writeStream;//this __strong may work!
i suggest u to change name of function
initNetworkCommunication to some

Just remove inputStream and outputStream from properties and let them stay only class variables.
In case of ARC, it helps.
#interface Scan: NSObject <NSStreamDelegate> {
NSInputStream * inputStream;
NSOutputStream * outputStream;
}
....
#end
In my case, it solved all problems. Was impossible to solve it other way.

Related

iOS5 : Why does setDelegate:self cause EXC_BAD_ACCESS in main.m?

EDIT: I actually traced it to the line where I call [in open]; and try to open the input stream. For some reason, my custom class may be an invalid delegate? I saw somebody else had the same error and did not have the problem after moving everything to a ViewController subclass rather than a custom class extending NSObject. However, I would still like to use my own custom class and not one of the ViewControllers.
I have a Connection class which I coded myself and I use NSInputStream and NSOutputStream. I initialize the streams in the init method:
- (id)init {
self = [super init];
if(self) {
messages = [NSMutableArray new];
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)HOST, PORT, &readStream, &writeStream);
[self setIn: (__bridge_transfer NSInputStream *)readStream];
[self setOut: (__bridge_transfer NSOutputStream *)writeStream];
NSLog(#"Streams opened.");
NSLog(#"ConnectionController initialized...");
}
return self;
}
Also, here's the definition for my connection class in the .h
#interface ConnectionController : NSObject <NSStreamDelegate> {
NSMutableArray *messages;
}
#property (strong, nonatomic) NSInputStream *in;
#property (strong, nonatomic) NSOutputStream *out;
-(void)sendMessage:(NSString*)msg;
-(void)stream:(NSStream *)eStream handleEvent:(NSStreamEvent)eventCode;
-(void)messageReceived:(NSString*)msg;
#end
When I call this method openStreams, I get EXC_BAD_ACCESS
- (void)openStreams {
[in setDelegate:self];
[out setDelegate:self];
[in scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[out scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[in open];
[out open];
}
When I comment out setDelegate:self, the error does not occur. However, I need this in order to use the event-driven method for handling NSStreamEvents
How do I fix this? Thanks!
What is happening is whatever instance of the Connection class (which is set to be the delegate) is being deallocated (causing EXC_BAD_ACCESS in the run loop) either because you didn't retain it, or you are using ARC (pretty likely) and you do not have a reference to it.
The solution is to either call retain on the connection class, like so:
Connection *connection = [[Connection alloc] init];
[connection retain];
Or if you do have ARC enabled, make an instance variable in the class where you alloc Connection, and store your instance of connection there. That way ARC will not dealloc it, because the instance var counts as a reference.

Realising object dynamically

I have a connection class that uses NSURLConnection to connect to the server. While in main class I call a class method of this class, the class method then allocates instance of itself and when the delegate ConnectionDidFinish is received, I release same class from within. Is this approach correct or this will lead to some problem.
Main Class :
[ConnectionClass connectToServer];
Connection Class :
#implementation ConnectionClass
+(void)connectToServer{
connectionClass = [[ConnectionClass alloc] init];
[connectionClass createConnection];
}
-(void)createConnection{
NSURLConnection *connection = [[NSURLConnection alloc] initWithDelegate:self];
// create asynchronous connection
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
[self release];
}
#end
Is it good to release self within its own method ?
What if I do it something this way;
Main Class :
[connectionClass setDelegate:self];
[connectionClass connectToServer];
Connection Class :
#implementation ConnectionClass
-(void)connectToServer{
[connectionClass createConnection];
}
-(void)createConnection{
NSURLConnection *connection = [[NSURLConnection alloc] initWithDelegate:self];
// create asynchronous connection
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
[self.delegate finishedConnection:self]; // added delegate and then called to the main class and pass the self object for main to release it
}
#end
And in the main class delegate we, release the object,
-(void)finishedConnection:(ConnectionClass*)connection
{
[connection release];
}
IS there any problem in releasing the object this way ?
[self release] & [self retain] sound totally crazy to me. It makes no sense at all IMHO.
And I don't see the point of making (void)connectToServer a class method !
Your second way is the way to go. You could also make one step of the two, creating a method like :
[connectionClass connectToServerWithDelegate:self];
I would do this:
#implementation ConnectionClass
+ (void)connectToServer {
connectionClass = [[ConnectionClass alloc] init];
[connectionClass createConnection];
[connectionClass release];
}
- (void)createConnection {
[self retain];
NSURLConnection *connection = [[NSURLConnection alloc] initWithDelegate:self];
// create asynchronous connection
[connection release];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self release];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[self release];
}
#end
That way the ConnectionClass object is self retaining, and you're not putting the retain/release responsibility in different places of code, that are not tightly related.
Edit: As Rabskatran points out, if you're just learning about retain/release, then this is not the optimal solution.
Your second example with the delegate is better. I'd let the connectionClass object be an instance variable, so you can message the connection object to cancel the operation when the main class (which would be the connection's delegate) gets deallocated.

NSStreams Crashing Program!

All,
I've run it down to this point by commenting, breakpoints, etc. The program crashes at the marked code.
-(void) initNetworkCommunication
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"192.168.17.1", 2004, &readStream, &writeStream);
inputStream = (NSInputStream *)readStream;
outputStream = (NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];//WHY MUST YOU CRASH HERE
[outputStream open];//WHY MUST YOU CRASH HERE ALSO!!?!?
NSLog(#"She be opened, sir!");
}
It doesn't crash if I comment out both of those, but it crashes if I comment out either one (so i.e. they are both causing the program to crash). There is no information that gets posted in the debugger either. All it does is send me to main.m and show me
"Thread 1: Program received signal: "EXC_BAD_ACCESS".
Thanks for the help in advance!
Edit: Here is my delegate method, but it doesn't even present the second active line in the log.
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
NSLog(#"stream event %i", streamEvent); //this doesn't post in the log when stream opened...
switch (streamEvent) {
case NSStreamEventOpenCompleted:
NSLog(#"Stream opened");
break;
case NSStreamEventHasBytesAvailable:
if (theStream == inputStream) {
uint8_t buffer[1024];
int len;
while ([inputStream hasBytesAvailable]) {
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0) {
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
if (nil != output) {
NSLog(#"server said: %#", output);
//[self messageReceived:output];
}
}
}
}
break;
case NSStreamEventErrorOccurred:
NSLog(#"Can not connect to the host!");
break;
case NSStreamEventEndEncountered:
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
//[theStream release];
theStream = nil;
break;
default:
NSLog(#"Unknown event");
}
}
What is happening is that the instance of the delegate class is being deallocated (causing EXC_BAD_ACCESS in the run loop) either because you didn't retain it, or you are using ARC (pretty likely) and you do not have a reference to it.
The solution is to either call retain on the delegate class, approximately like so:
SomeStreamDelegate *theDelegate = [[SomeStreamDelegate alloc] init];
[theDelegate retain];
Or if you do have ARC enabled, make an instance variable in the class where you alloc the delegate, and store your instance of connection there. That way ARC will not dealloc it, because the instance var counts as a reference.
If you are using ARC, cast the streams like this :
inputStream = (__bridge NSInputStream *)readStream;
outputStream = (__bridge NSOutputStream *)writeStream;
This should prevent the crash. And keep in mind that if your streams are owned by a separate thread than the main thread that means the run loop needs to be called manually using run method after opening the streams.
When I placed this in my View Controller (and not in a separate class) it worked perfectly.
I had a similar issue where my app would crash in the -handleEvent callback with a huge streamEvent number. I resolved it by making sure I initialize the NSStream objects (input and output) AND open a connection to the server in the -init method of the NetworkClient object which my VC plans to use.
Try this out once,
NSInputStream * inputStream = objc_unretainedObject(readStream);
May be a casting issue

iphone :client-server communication not occuring

i had made the following programming for client server programming but it is not working. the server is not able to receive the request for connection setup.plz help.
#import "clientserverprogramViewController.h"
#import "secondview.h"
#import <CoreFoundation/CFSocket.h>
#include <sys/socket.h>
#include <netinet/in.h>
NSInputStream *iStream;
NSOutputStream *oStream;
#implementation clientserverprogramViewController
#synthesize name,filepath,display;
-(IBAction) print {
NSString *urlStr = serverIP;]
[display setText : urlStr];
if (![urlStr isEqualToString:#""]) {
NSURL *website = [NSURL URLWithString:urlStr];
if (!website) {
NSLog(#"%# is not a valid URL");
return;
}
NSHost *host = [NSHost hostWithName:[website host]];
[NSStream getStreamsToHost:host port:3000 inputStream:&iStream outputStream:&oStream];
[iStream retain];
[oStream retain];
[iStream setDelegate:self];
[oStream setDelegate:self];
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[oStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[iStream open];
[oStream open];
}
}
-(IBAction) settings {
[self presentModalViewController:nextview animated: YES];
}
-(IBAction) cancel {
exit(0);
}
- (void)dealloc {
[super dealloc];
}
#end
You only open streams and don't do anything with them. It's like picking up a phone and not dialing a number. Use NSStreamDelegate protocol to implement data transmission code.
Update:
You have these lines that set the delegate for streams:
[iStream setDelegate:self];
[oStream setDelegate:self];
Now implement methods that are defined in NSStreamDelegate protocol in your own class (AFAIK - there's only one of them). See how to receive/send data from there.
Is there a specific reason you're using streams?
What about using NSURLConnection? Here's a piece of code from a project of mine. Both are in KANetworkManager. KANetworkTransactionType is simply a enum that helps me know how to parse the response.
+ (void) createAndStartUrlConnection:(NSMutableURLRequest *)request type:(KANetworkTransactionType)type target:(id)target callback:(SEL)callback;
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSDictionary *requestDict = [NSDictionary dictionaryWithObjectsAndKeys:request, #"request", [NSNumber numberWithInt:type], #"type", target, #"target", [NSValue valueWithPointer:callback], #"callback", nil];
[KANetworkManager performSelectorInBackground:#selector(makeNetworkCall:) withObject:requestDict];
}
I'm able to made a synchronous network call because I always call this method on its own thread. It's a simpler way to achieve asynchronous network communications without dealing with delegates (although the delegate method provides some benefits). Your parseResponse method would need to be specific to whatever your web service it sending back. parseResponse would notify the callback method. Let me know if you have additional questions regarding this.
+ (void) makeNetworkCall:(NSDictionary *)params
{
// We assume this method won't be called from the main thread, so we need our own NSAutoreleasePool.
NSAutoreleasePool *autoreleasePool = [[NSAutoreleasePool alloc] init];
NSMutableURLRequest *request = [params objectForKey:#"request"];
KANetworkTransactionType type = [(NSNumber *)[params objectForKey:#"type"] intValue];
id target = [params objectForKey:#"target"];
SEL callback = (SEL)[[params objectForKey:#"callback"] pointerValue];
NSURLResponse *response;
NSError *err;
// We make a synchronous request assuming we're on a background thread.
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
if (data.length > 0)
{
[self parseResponse:data type:type target:target callback:callback];
}
else
{
NSLog(#"Error occured during network call. %#", err);
}
[autoreleasePool drain];
}
JB gates,
In your code you inform iStream and oStream that your clientserverprogramViewController object is to be the delegate for each. However, a proper delegate needs actual implementation. Your class needs to implement this method:
– stream:handleEvent:
The details are documented here:
Reading From Input Streams
Writing To Output Streams
Also, your code will not work on a real iPhone. There is an updated Core Foundation API for creating the socket pair, details here.
Update
Just wondering if this is not a software issue but maybe the server is behind a firewall. Please give details what the server is, ie webserver, netcat, or simple TCP socket, etc.
Peter

iPhone TCP/IP Socket Server/Client Program

I have read a lot of questions regarding this subject on this website however they didn't quiet answer my question. If you can't be ### about my goal or background skip to the question.
My Goal
Is to build a server that can run on Mac OS X 10.4+ and later, port it to Windows XP/Vista (no idea how to do that yet, but that's a problem for later).
Then let the iPhone be the client that is able to see the computer names that are running the server (through WiFi). The user of the iPhone can then select the computer name to connect to the server on that computer.
After that they can send simple text messages to each other. For example, the iPhone sends 'Knock Knock' and the server responds 'Who is there?'. Or a simple client: 'Ping', server responds 'Pong' will do just fine.
Background
I have worked with sockets in the past, but only in Visual Basic 6 with the WINSOCKET.dll it was very easy to create a TCP/IP server.
server.host = localhost;
server.port = 12203;
server.listen();
With the client I only needed to do the following to connect.
client.connect(localhost, 12203);
There were some callbacks available like connect, close, dataArrival, etc. which I could use to do anything I want.
Perhaps for the iPhone there are libraries written for it, but is it that hard to create this simple application yourself? After doing some research I understand that I have to look in the area of CFNetwork, CFHost, CFSocket, CFStream.
Question
Is there anyone that could guide me to a tutorial or post the code where you have two buttons on the iPhone. [ Start Server ] and [ Connect to Server] where the first will start a TCP/IP server on a certain port and the second connects to it.
After a connection has been made maybe also the code to send a simple 'Ping'-message to the server after the server receives this responds with a 'Pong'-message to the client.
That would really be helpful. But maybe I am asking for to much here.
this tutorial to create a chat sample app works very well and is pretty straightforward (any iphone noob, like me, can make it work, EVEN IN SIMULATOR MODE it connects to external socket server).
i adapted it to talk my socket server and it works like a charm. this is test code, so there's no real concern with loose ends. it only sends one message (your logon id) and receives an answer back, which it shows in the console.
//
// ViewController.m
// zdelSocketTest01a
//
//
#import "ViewController.h"
#implementation ViewController
#synthesize inputNameField;
#synthesize joinView;
- (void)initNetworkCommunication {
uint portNo = 5555;
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"227.3.4.56", portNo, &readStream, &writeStream);
inputStream = (__bridge NSInputStream *)readStream;
outputStream = (__bridge NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self initNetworkCommunication];
messages = [[NSMutableArray alloc] init];
}
- (void)viewDidUnload
{
[self setInputNameField:nil];
[self setJoinView:nil];
[self setJoinView:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (IBAction)joinChat:(id)sender {
NSString *response = [NSString stringWithFormat:#"logon,%#", inputNameField.text];
NSData *data = [[NSData alloc] initWithData:[response dataUsingEncoding:NSASCIIStringEncoding]];
[outputStream write:[data bytes] maxLength:[data length]];
}
/*
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
NSLog(#"stream event %i", streamEvent);
}
*/
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
typedef enum {
NSStreamEventNone = 0,
NSStreamEventOpenCompleted = 1 << 0,
NSStreamEventHasBytesAvailable = 1 << 1,
NSStreamEventHasSpaceAvailable = 1 << 2,
NSStreamEventErrorOccurred = 1 << 3,
NSStreamEventEndEncountered = 1 << 4
};
uint8_t buffer[1024];
int len;
switch (streamEvent) {
case NSStreamEventOpenCompleted:
NSLog(#"Stream opened now");
break;
case NSStreamEventHasBytesAvailable:
NSLog(#"has bytes");
if (theStream == inputStream) {
while ([inputStream hasBytesAvailable]) {
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0) {
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
if (nil != output) {
NSLog(#"server said: %#", output);
}
}
}
} else {
NSLog(#"it is NOT theStream == inputStream");
}
break;
case NSStreamEventHasSpaceAvailable:
NSLog(#"Stream has space available now");
break;
case NSStreamEventErrorOccurred:
NSLog(#"Can not connect to the host!");
break;
case NSStreamEventEndEncountered:
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
break;
default:
NSLog(#"Unknown event %i", streamEvent);
}
}
/*
- (void) messageReceived:(NSString *)message {
[messages addObject:message];
[self.tView reloadData];
}
*/
#end
your ViewController.h file would contain
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <NSStreamDelegate>
#property (weak, nonatomic) IBOutlet UITextField *inputNameField;
#property (weak, nonatomic) IBOutlet UIView *joinView;
- (IBAction)joinChat:(id)sender;
#end
NSInputStream *inputStream;
NSOutputStream *outputStream;
NSMutableArray * messages;
NOOBS ONLY: you must link your button and text field by pressing CONTROL and dragging the object into the code window. when you do that, the properties above will automatically be created. check this video tutorial if you are stumped
NOOBS ONLY 2: this socket will output in the CONSOLE PANE of XCODE. on the upper right hand corner of your xcode window, click HIDE OR SHOW THE DEBUG AREA (ask for help if necessary).
built and tested (simulator and device) on a macbook with 2GB memory, using xcode 4.2 for snow leopard.
I recommend the the following:
Cocoa Async Socket
There's also a basic example project on the site to get you started. I've had good success working with that framework.
I would expect you would want your server to already be started, and then you would only need a "Connect to Server" button, and then your "Ping". Otherwise, you need a separate process on your server box which responds to the "Start Server" message and starts the server.