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

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.

Related

Mememory Corruption in #property

I have the next piece of code, one iVar with this property retained and released in it's class dealloc method. The iVar is used in 2 methods and continually change the value but
sometimes when I use the value is corrupted. Why is that?
.h
#interface ChatController : NSObject <ASIHTTPRequestDelegate>{
NSTimer *timer;
NSString *_idLastMessageFromServer;
}
#property(nonatomic, retain)NSString *idLastMessageFromServer;
#end
.m
#implementation ChatController
#synthesize idLastMessageFromServer = _idLastMessageFromServer;
- (void)initLoopTimer{
timer = [NSTimer timerWithTimeInterval:5 target:self selector:#selector(update:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}
- (void)update:(id)sender{
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:CONSTANT_YYYY];
[request setDelegate:self];
[request addPostValue:_idLastMessageFromServer forKey:CONSTANT_XXX];
[request setDidFinishSelector:#selector(requestUpdateFinish:)];
[request startAsynchronous];
}
- (void)requestUpdateFinish:(ASIHTTPRequest *)request{
NSString *response = [request responseString];
if(response && response.length){
if(![response isEqualToString:CHAT_RESPONSE_NO_MESSAGES]){
NSArray *array = [response componentsSeparatedByString:CHAT_PARSE_RESPONSE];
if(array && [array count] == 2){
**_idLastMessageFromServer = [array objectAtIndex:0];**
}
}
}
}
But when the loop calls the method update:, it crashes in this line of code
[request addPostValue:_idLastMessageFromServer forKey:CONSTANT_XXX];
with EXC_BAD_ACCESS message, but why?
This line:
_idLastMessageFromServer = [array objectAtIndex:0];
should probably be
self.idLastMessageFromServer = [array objectAtIndex:0];
This would access the property instead of the variable directly, thus triggering the retain / release during the assignment. Otherwise, the pointer is assigned, but the object taken from the array is not retained, it may quickly become invalid and you are left with a pointer pointing to a released object.
By using _idLastMessageFromServer instead of self.idLastMessageFromServer, you are not retaining the string. The allows the retain count to drop to zero which deallocates the object. At that point you have a reference to bad memory, hence the app crashes.
Don't use iVars directly unless you have a good reason (like -init or -dealloc). Use the property instead.
[request addPostValue:self.idLastMessageFromServer forKey:CONSTANT_XXX];
and
self.idLastMessageFromServer = [array objectAtIndex:0];
I'll add a bit more of a detailed explanation about properties.
self.idLastMessageFromServer when used to read the value of the property calls an auto generated method -idLastMessageFromServer. This method will look something like:
- (NSString *)idLastMessageFromServer
{
return _idLastMessageFromServer;
}
self.idLastMessageFromServer when used to set the value of the property calls an auto generated method -setIdLastMessageFromServer:. This method will look something like:
- (void)setIdLastMessageFromServer:(NSString *)idLastMessageFromServer
{
if (_idLastMessageFromServer != idLastMessageFromServer) {
[_idLastMessageFromServer release];
_idLastMessageFromServer = idLastMessageFromServer;
[_idLastMessageFromServer retain];
}
}
One final note: be sure to release _idLastMessageFromServer in your -dealloc method. Something like:
- (void)dealloc
{
[_idLastMessageFromServer release];
[super dealloc];
}
More details about properties and iVars.
Properties (like self.idLastMessageFromServer) are simply a easy way to handle getter and setter methods. They cannot hold data because they are methods. iVars (like _idLastMessageFromServer) are a pointer to a location in memory. They cannot control access and maintain state because they are simply a pointer.
Properties and iVars work together.
The line #property(nonatomic, retain) NSString *idLastMessageFromServer; says that somewhere in my implementation, my class will have a getter and setter for the property idLastMessageFromServer.
The line #synthesize idLastMessageFromServer = _idLastMessageFromServer; auto generates the getter and setter methods for idLastMessageFromServer using the iVar _idLastMessageFromServer.
In short, the property controls access to the iVar; the iVar is the storage location for the property.

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.

EXC_Bad_Access When Calling Method from Class

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.

How to return an object from a class that uses NSURLConnection and it's delegate classes?

I'm in the process of trying to move code from a UITableViewController class to a "helper" class.
The code utilizes NSURLConnection to grab and parse JSON and then populate an NSMutableArray.
What I'd like to do is call a method in my helper class that returns a NSMutableArray. What I don't understand is how to return the array from the connectionDidFinishLoading delegate class of NSURLConnection (where the array is actually built) as though it was from the originally called method that started the connection. In other words, how does the method that calls NSURLConnection get control back so it can return a value from the whole operation?
Here are the relevant methods from the helper class. How do I get the getMovies method to return the listOfMovies that is built in the connectionDidFinishLoading delegate class?
-(NSMutableArray)getMovies:(NSURL*)url {
responseData = [[NSMutableData data] retain];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//NSURLRequest* request = [NSURLRequest requestWithURL: url cachePolicy: NSURLRequestUseProtocolCachePolicy timeoutInterval: 30.0];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (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 {
//TODO error handling for connection
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
//---initialize the array---
listOfMovies = [[NSMutableArray alloc] init];
tmdbMovies = [[NSArray alloc] init];
posters = [[NSArray alloc] init];
thumbs = [[NSDictionary alloc] init];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
SBJsonParser *json = [[SBJsonParser new] autorelease];
tmdbMovies = [json objectWithString:responseString];
// loop through all the top level elements in JSON
for (id movie in tmdbMovies) {
// 0 - Name
// 1 - Meta
// 2 - Url
if ((NSNull *)[movie objectForKey:#"name"] != [NSNull null]) {
if (![[movie objectForKey:#"name"] isEqualToString:#""]) {
name = [movie objectForKey:#"name"];
}
}
if ((NSNull *)[movie objectForKey:#"info"] != [NSNull null]) {
if (![[movie objectForKey:#"info"] isEqualToString:#""]) {
meta = [movie objectForKey:#"info"];
}
}
if ((NSNull *)[movie objectForKey:#"thumb"] != [NSNull null]) {
if (![[movie objectForKey:#"thumb"] isEqualToString:#""]) {
thumbUrl = [movie objectForKey:#"thumb"];
}
}
NSLog(#"Name: %#", name);
NSLog(#"Info: %#", meta);
NSLog(#"Thumb: %#", thumbUrl);
NSMutableArray *movieData = [[NSMutableArray alloc] initWithObjects:name,meta,thumbUrl,nil];
// add movieData array to listOfJMovies array
[listOfMovies addObject:movieData];
[movieData release];
}
//FIXME: Connection warning
if (connection!=nil) {
[connection release];
}
[responseData release];
[responseString release];
}
What you really need to do here is create a #protocol that creates a delegate for your helper class. Then change -(NSMutableArray)getMovies:(NSURL*)url to -(void)getMovies:(NSURL*)url
The class that is calling your helper method needs to implement your helper method's delegate.
Then - (void)connectionDidFinishLoading:(NSURLConnection *)connection calls the delegate method(s). It's best to have a one for success and one for failure.
=Update Begin=
You will need to also define an id delegate in your helper file which the calling class sets to self after init but before calling -(void)getMovies:(NSURL*)url. That way the helper file knows where to call back to.
getMovies *movieListCall = [[getMovies alloc] init];
movieListCall.delegate = self;
[movieListCall getMovies:<your NSURL goes here>];
You will see some additional lines for the inclusion of a delegate in both the getMovies.h and getMovies.m files.
=Update End=
in your getMovies.h file add:
#protocol getMoviesDelegate
#required
- (void)getMoviesSucceeded:(NSMutableArray *)movieArray;
- (void)getMoviesFailed:(NSString *)failedMessage;
#end
#interface getMovies : NSOBject {
id delegate;
}
#property (nonatomic, assign) id delegate;
in your getMovies.m file add:
#synthesize delegate;
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
//TODO error handling for connection
if ([delegate respondsToSelector:#selector(getMoviesFailed:)]) {
[delegate getMoviesFailed:[error localizedDescription]];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
//finishes with
if ([delegate respondsToSelector:#selector(getMoviesSucceeded:)]) {
[delegate getMoviesSucceeded:listOfMovies];
}
}
update your calling class .h file to use getMoviesDelegate:
#interface MoviesView : UIViewController <getMoviesDelegate>{
.
.
.
}
add the getMoviesDelegate methods to your calling class .m file
- (void)getMoviesSucceeded:(NSMutableArray *)movieArray {
//deal with movieArray here
}
- (void)getMoviesFailed:(NSString *)failedMessage {
//deal with failure here
}
This is not tested but hopefully gives you a road map to work with.
Protocols are nice because you can make both required and optional delegate methods and it helps in refining your helper methods to become very reusable across projects. The compiler will also warn you if you have implemented a protocol but not implemented the protocol's required delegate methods. If you follow this path be sure to use conformsToProtocol: and respondsToSelector:
Fundamentally, what's happening is that you're starting an asynchronous network load (asynchronous is the right way to do this, almost assuredly), and then you need some way to resume whatever operation you were doing before the load began. You have a few options:
Create your own delegate protocol. Your UITableViewController would then set itself as the helper's delegate, and the helper would call helperDidLoad or whatever you named that method. There's more information on writing delegates in the Cocoa Programming Guide.
Use blocks and continuation passing style. This is a bit more advanced but I like it. In your UITableViewController you'd write something like this:
[helper doSomething:^ (id loaded) {
[modelObject refresh:loaded]; // or whatever you need to do
}];
And then in your helper you'd write:
- (void)doSomething:(void ^ (id))continuation {
_continuation = continuation;
//kick off network load
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
_continuation(_data);
}
Use notifications. Read the NSNotificationCenter docs.
Use KVO. The KVO programming guide has a lot of good info on Key-Value Observing.
How to i get the getMovies method to return the listOfMovies that is built in the connectionDidFinishLoading delegate class?
I'm going to argue that you should not do that.
Network requests should be made asynchronously. If your getMovies were to make a synchronous request and return only when it had data you would block that entire thread while you waiting for a network connection to finish. This is a bad idea in general and a terrible idea if your main thread is calling getMovies. Blocking the main thread will prevent you from responding to touches or updating the UI, your app will appear frozen, and the OS will terminate it if your users don't quit in frustration first.
Instead have the helper class notify the caller when data is available (or when it failed to retrieve data) through a delegate call back, notification, KVO, or whatever mechanism you prefer.
Here are the steps, pseudocode like style:
[helperInstance setDelegate:self]; // where self is your UITableViewController class
in your helper class, in the connectionDidFinishLoading do something like this:
[delegate finishedLoadingData:JSONData];
Also you can define a protocol for your delegate, and the declare the delegate like this in your helper class:
#property (nonatomic, assign) id<YourProtocol> delegate;
Hope this helps,
Moszi

What could cause this difference in behaviour from iphone OS3.0 to iOS4.0?

I am getting a strange EXC_BAD_ACCESS error when running my app on iOS4. The app has been pretty solid on OS3.x for some time - not even seeing crash logs in this area of the code (or many at all) in the wild.
I've tracked the error down to this code:
main class:
- (void) sendPost:(PostRequest*)request {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSURLResponse* response;
NSError* error;
NSData *serverReply = [NSURLConnection sendSynchronousRequest:request.request returningResponse:&response error:&error];
ServerResponse* serverResponse=[[ServerResponse alloc] initWithResponse:response error:error data:serverReply];
[request.objectToNotifyWhenDone performSelectorOnMainThread:request.targetToNotifyWhenDone withObject:serverResponse waitUntilDone:YES];
[pool drain];
}
(Note: sendPost is run on a separate thread for each invocation of it. PostRequest is just a class to encapsulate a request and a selector to notify when complete)
ServerResponse.m:
#synthesize response;
#synthesize replyString;
#synthesize error;
#synthesize plist;
- (ServerResponse*) initWithResponse:(NSURLResponse*)resp error:(NSError*)err data:(NSData*)serverReply {
self.response=resp;
self.error=err;
self.plist=nil;
self.replyString=nil;
if (serverReply) {
self.replyString = [[[NSString alloc] initWithBytes:[serverReply bytes] length:[serverReply length] encoding: NSASCIIStringEncoding] autorelease];
NSPropertyListFormat format;
NSString *errorStr;
plist = [NSPropertyListSerialization propertyListFromData:serverReply mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&errorStr];
}
return self;
}
ServerResponse.h:
#property (nonatomic, retain) NSURLResponse* response;
#property (nonatomic, retain) NSString* replyString;
#property (nonatomic, retain) NSError* error;
#property (nonatomic, retain) NSDictionary* plist;
- (ServerResponse*) initWithResponse:(NSURLResponse*)response error:(NSError*)error data:(NSData*)serverReply;
This reliably crashes with a bad access in the line:
self.error=err;
...i.e. in the synthesized property setter!
I'm stumped as to why this should be, given the code worked on the previous OS and hasn't changed since (even the binary compiled with the previous SDK crashes the same way, but not on OS3.0) - and given it is a simple property method.
Any ideas? Could the NSError implementation have changed between releases or am I missing something obvious?
The setter calls [retain] on the new value, and [release] on the old value. One of those must be invalid (and non-nil) to cause the bad access.
sendPost doesn't initialize it's local error variable and if it is not set by NSURLConnection then it will contain garbage. Try initializing error to nil in sendPost.
Do you ever free serverResponse in the sendPost: message?
You init never calls its parent init. Try something like:
- (ServerResponse*) initWithResponse:(NSURLResponse*)resp error:(NSError*)err data:(NSData*)serverReply
{
if (self = [super init])
{
// ....
}
return self;
}