Delegate Methods not called in Class Instance - iphone

I'm creating an instance of a class called S3ObjectController (S3OC) that has one method and four delegate methods. I create an instance of my S3OC, call an instance method from the S3OC Class (which I know fires from NSLog statements) but none of the associated delegate methods are called within the S3OC class. I have the delegate set to self in the method and the delegate declared properly in the .h header.
Thoughts? just to be clear, it's the (void)request methods in the .m file below I'm thinking should be called that aren't. I'm getting EXC BAD ACCESS errors. Is self getting released by ARC?
The entire .m file of the S3OC class is below:
#import "S3ObjectController.h"
#implementation S3ObjectController
#synthesize string;
#synthesize s3GOR, s3Client;
-(void)method
{
NSLog(#"Method Called");
s3Client = [[AmazonS3Client alloc] initWithAccessKey:ACCESS_KEY_ID withSecretKey:SECRET_KEY];
s3GOR = [[S3GetObjectRequest alloc]initWithKey:string withBucket:[Constants pictureBucket]];
[s3GOR setDelegate:self];
[s3Client getObject:s3GOR];
NSLog(#"Method Finished");
}
-(void)request:(AmazonServiceRequest *)request didFailWithError:(NSError *)error
{
NSLog(#"Error %#",error);
}
-(void)request:(AmazonServiceRequest *)request didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"Response Key %#", response);
}
-(void)request:(AmazonServiceRequest *)request didReceiveData:(NSData *)data
{
NSLog(#"ObjectRequestKey = %#",request);
}
-(void)request:(AmazonServiceRequest *)request didCompleteWithResponse:(AmazonServiceResponse *)response
{
NSLog(#"Final Delegate Method");
}
Here's the header:"
#interface S3ObjectController : NSObject <AmazonServiceRequestDelegate>{
NSMutableData *responseData;
NSString *string;
AmazonS3Client *s3Client;
S3GetObjectRequest *s3GOR;
}
-(void)method;
#property (nonatomic, strong) NSString *string;
#property (nonatomic, strong) S3GetObjectRequest *s3GOR;
#property (nonatomic, strong) AmazonS3Client *s3Client;
#end
Finally, here's how I call the method in another class:
for (NSString *name in nameArray){
#try {
S3ObjectController *localS3 = [[S3ObjectController alloc]init];
localS3.string = name;
[localS3 method];
NSLog(#"called");
}

I think your suspicions about ARC are true. Because delegate properties are usually weak references, they aren't enough to keep the object from being released.
Make an NSArray that's an iVar and add the S3ObjectController to it. If the delegates still don't fire, you know it's something else...
Edit:
so declare an NSMutableArray in the header of the class that contains your for loop, initialize it somewhere like this:
myArray = [NSMutableArray arrayWithCapacity:0];
then use it like this:
for (NSString *name in nameArray){
#try {
S3ObjectController *localS3 = [[S3ObjectController alloc]init];
localS3.string = name;
[localS3 method];
[myArray addObject:localS3];
NSLog(#"called");
}
}

Related

Implementing NSURLConnectionDataDelegate Protocol

I am new to iOS development. I am trying to implement NSURLConnectionDataDelegate Protocol but it seems that none of the delegate methods ever get called. I had to type the delegate methods in myself, is it supposed to be automatically generated?
I have an NSLog command in each delegate method but nothing prints. I am using NSURLConnection to Asynchronously download and keep track of the progress so I can update a progressView later.
SearchFeed.h file (Notice I have tried to implement the protocol when I typed NSURLConnectionDataDelegate
#import <Foundation/Foundation.h>
#import "Doc.h"
#interface SearchFeed : NSObject <NSXMLParserDelegate, NSURLConnectionDataDelegate>
{
NSMutableString * currentElementValue;
Doc *currentDoc;
}
#property(strong,nonatomic) NSURL * searchUrl;
#property(strong,nonatomic) NSArray * searchResults;
//#property(retain, nonatomic) Doc * currentDoc;
#property(retain, nonatomic) NSMutableArray *docs;
//#property(retain, nonatomic) NSURLConnection *urlConnection;
#property(retain, nonatomic) UIProgressView * progressBar;
-(void)retrieveFromInternet;
-(double) getProgress;
+(NSString *)pathToDocuments;
+(void)downloadPDFToMyDocumentsFrom:(NSString*) PDFUrl filename:(NSString *) title;
+(NSArray *)listFilesAtPath:(NSString *)path;
#end
SearchFeed.m file:
#import "SearchFeed.h"
#implementation SearchFeed
#synthesize searchUrl = _searchUrl; //where to search from
#synthesize searchResults = _searchResults; // Not being used -- I think
//#synthesize currentDoc = _currentDoc; //current Doc
#synthesize docs = _docs; //array of Docs
#synthesize progressBar = _progressBar;
NSURLConnection *urlConnection;
double fileLength =0;
double lastProgress =0;
double currentLength =0;
NSOutputStream *fileStream;
+(void)downloadPDFToMyDocumentsFrom:(NSString*) PDFUrl filename:(NSString *) title {
NSURL *url = [[NSURL alloc] initWithString:PDFUrl];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
NSString *fileName = [title stringByAppendingPathExtension:#"pdf"];
NSString *filePath = [[self pathToDocuments] stringByAppendingPathComponent:fileName];
fileStream = [[NSOutputStream alloc] initToFileAtPath:filePath append:YES];
[fileStream open];
}
//handling incoming data
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
double length = [data length];
currentLength += length;
double progress = currentLength/fileLength;
NSLog(#"Receiving data");
if(lastProgress < progress)
{
//progressBar WRITE code to update the progress for the progress bar
lastProgress = progress;
self.progressBar.progress = lastProgress;
NSLog(#"%f -------------------------------------------------------", lastProgress);
}
NSUInteger left = [data length];
NSUInteger nwr = 0;
do {
nwr = [fileStream write:[data bytes] maxLength:left];
if(nwr == -1)
break;
left -= nwr;
}while(left>0);
if(left)
{
NSLog(#"Stream error: %#", [fileStream streamError]);
}
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
long length = [response expectedContentLength];
fileLength = length;
NSLog(#"%f ------------------------------------------------------- is the fileLength", fileLength);
}
//handling connection progress
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
//WRITE code to set the progress bar to 1.0
self.progressBar.progress = 1.0;
[fileStream close];
NSLog(#"%f -------------------------------------------------------", lastProgress);
}
I have set the delegate for NSURLConnection urlConnection to self which is SearchFeed.m class.
In SearchFeed.h, I tried to implement the NSURLConnectionDataDelegate protocol.
I had to create connectionDidFinishLoading, didReceiveResponse and didReceiveData methods but those methods don't get called.
I either have not implemented the protocol properly OR I have declared some methods as + and some as - (some methods are class methods while some are instance methods)
downloadPDFToMyDocumentsFrom is a class method which is invoked when the user clicks download.
This method sets the NSURLConnection, sets the URL etc and the delegate and opens the fileStream to receive data. However, none of the other methods get called.
Your downloadPDFToMyDocumentsFrom method is setup as a class method (+), and you setup your delegate to be self, meaning the Class in this case. You should make the downloadPDFToMyDocumentsFrom method a instance method (-) so that self is an instantiated object.

Instances of NSObject acting as NSUrlConnection delegate appear not to be isolated

First post here, so I hope it is detailed enough.
While developing an Iphone App I am confronted with some strange behavour. A member variable of a certain instance of my "WebserviceConnection" class seems to obtain the value I assign to another instances of the same class.
For illustration: This is en excerpt of my log. I assume the 0x000000 is an instance ID. The fourth response should be "<-: 1".
2011-11-03 16:25:13.227 Dashboard[540:707] ->: 1, <WebserviceConnection: 0x11f950>
2011-11-03 16:25:13.256 Dashboard[540:707] ->: 0, <WebserviceConnection: 0x323db0>
2011-11-03 16:25:15.318 Dashboard[540:707] <-: 0, <WebserviceConnection: 0x323db0>
2011-11-03 16:25:15.325 Dashboard[540:707] <-: 0, <WebserviceConnection: 0x11f950>
The class is a NSUrlConnection delegate which exhibits this behavour when two connections are open at the same time.
This class: WebserviceConnection.h
(The ConnectionType is an enum)
#import "WebserviceConnection.h"
#import "WebserviceUtility.h"
#implementation WebserviceConnection
BOOL isCanceled;
NSDictionary *result;
ConnectionType connectionType;
id <WebserviceConnectionDelegate> delegate;
- (id)initWithDelegate:(id)webServiceDelegate connectionType:(ConnectionType) type {
delegate = webServiceDelegate;
connectionType = type;
isCanceled = NO;
NSLog(#"->: %i, %#", connectionType, self);
return self;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
switch (connectionType) {
case GetAllAlerts:
result = [WebserviceUtility getJsonFromData:data];
break;
case GetServerAlerts:
result = [WebserviceUtility getJsonFromData:data];
break;
case GetServers:
result = [WebserviceUtility getJsonFromData:data];
break;
default:
result = nil;
break;
}
}
- (void)displayErrorAlert {
UIAlertView *errorMessage = [[UIAlertView alloc] initWithTitle:#"Fout" message:#"Verbinding met webservice niet mogelijk" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[errorMessage show];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
if(!isCanceled) {
#try {
[delegate connection:connection ofType:connectionType didFinishWithError: [NSDictionary dictionaryWithObject:#"error" forKey:#"WebserverConnectionFailed"]];
}
#catch (NSException *e) {}
#finally {}
[self displayErrorAlert];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"<-: %i, %#", connectionType, self);
if(!isCanceled) {
[delegate connection:connection ofType:connectionType didFinishWithResult:result];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
NSURLCredential *credential = [WebserviceUtility getCredentials];
if ([challenge previousFailureCount] == 0) {
[[challenge sender] useCredential:credential
forAuthenticationChallenge:challenge];
}
else {
[delegate connection:connection ofType:connectionType didFinishWithError: [NSDictionary dictionaryWithObject:#"error" forKey:#"WebserverConnectionFailed"]];
[self displayErrorAlert];
}
}
- (void)delegateDidDealloc {
NSLog(#"!!: %i, %#", connectionType, self);
isCanceled = YES;
}
#end
Used like this:
- (void) getAllAlerts {
NSURLRequest *request = [WebserviceUtility getRequestForPath:#"/dashboard/DashboardAppleConnector.asmx/GetActiveAlerts"];
webserviceConnection = [[WebserviceConnection alloc] initWithDelegate:self connectionType:GetAllAlerts];
connection = [[NSURLConnection alloc] initWithRequest:request delegate: webserviceConnection];
}
When another ViewController with its own webserviceConnection instance uses its instance (similar to getAllAlerts) all goed pearshaped!
Any thoughts anyone?
Regards,
Bert
It looks like the problem is happening because of the way you are declaring your variables like connectionType. If you want them to be declared as instance variables, you should be putting them in the interface declaration:
#interface WebServiceConnection {
BOOL isCanceled;
NSDictionary *result;
ConnectionType connectionType;
id <WebserviceConnectionDelegate> delegate;
}
#end
By declaring them in the #implementation block you are actually creating global variables, not instance variables.
See this SO post for more information
The definition block:
BOOL isCanceled;
NSDictionary *result;
ConnectionType connectionType;
id <WebserviceConnectionDelegate> delegate;
Declares those four things to be global variables, exactly as if they weren't in the #implementation block. Simply putting things inside #implementation doesn't make them local to the object — it just explains which object all the subsequent method implementations belong to.
If you don't mind putting implementation specifics into your header files, you could move them into the #interface declaration, e.g.
#interface WebserviceConnection
{
BOOL isCanceled;
NSDictionary *result;
ConnectionType connectionType;
id <WebserviceConnectionDelegate> delegate;
}
// etc
#end
You can keep them purely internal to the implementation at the cost of some repetitive syntax by adding them to your class through a category, e.g.
#import "WebserviceConnection.h"
#import "WebserviceUtility.h"
#interface WebserviceConnection() // a category to add additional properties
#property (nonatomic, assign) BOOL isCanceled;
#property (nonatomic, retain) NSDictionary *result;
#property (nonatomic, assign) ConnectionType connectionType;
#property (nonatomic, assign) id <WebserviceConnectionDelegate> delegate;
#end
#implementation WebserviceConnection
// synthesising the properties also adds the named properties as instance variables
#synthesize isCanceled;
#synthesize result;
#synthesize connectionType;
#synthesize delegate;
- (id)initWithDelegate:(id)webServiceDelegate ... etc...
Aside: a method called getJsonFromData: should return a non-owning reference according to Cocoa naming conventions since it doesn't contain 'new', 'alloc', 'retain' or 'create'. Which, if you were to obey, would leave you with a dangling pointer in result in the code as presented.

Save own Class with NSCoder

I'm trying to store some custom class/data to a file in my iPhone/iPad app.
I have a Class RSHighscoreList
#interface RSHighscoreList : NSObject {
NSMutableArray *list;
}
which contains objects of RSHighscore in the list
#interface RSHighscore : NSObject {
NSString *playerName;
NSInteger points;
}
When I try to store all to file
- (void)writeDataStore {
RSDataStore *tmpStore = [[RSDataStore alloc] init];
_tmpStore.highscorelist = self.highscorelist.list;
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:tmpStore forKey:kDataKey];
[archiver finishEncoding];
[data writeToFile:[self dataFilePath] atomically:YES];
[archiver release];
[data release];
}
#interface RSDataStore : NSObject <NSCoding, NSCopying> {
NSMutableArray *highscorelist;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:highscorelist forKey:#"Highscorelist"];
}
The app will crash with an error message
-[RSHighscore encodeWithCoder:]: unrecognized selector sent to instance 0x573cc20
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[RSHighscore encodeWithCoder:]: unrecognized selector sent to instance 0x573cc20'
I wonder why the error tells about RSHighscore, even if that is 'wrapped'. Does anyone have a good idea?
RSDataStore has an -encodeWithCoder: method, but (according to the error message) RSHighscore doesn't. You need to implement the NSCoding protocol for every class you're serializing.
#implementation RSHighscore
static NSString *const kPlayerName = #"PlayerName";
static NSString *const kPoints = #"Points";
-(id)initWithCoder:(NSCoder *)decoder {
if ((self=[super init])) {
playerName = [[decoder decodeObjectForKey:kPlayerName] retain];
points = [decoder decodeIntegerForKey:kPoints];
}
return self;
}
-(void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:playerName forKey:kPlayerName];
[encoder encodeInt:points forKey:kPoints];
}
...
If the base class of RSHighscore is ever changed to something other than NSObject, the -initWithCoder: method might need to be changed to call [super initWithCoder:decoder] rather than [super init]. Alternatively, add <NSCoding> to NSObject and change RSHighscore's -initWithCoder: now.
#interface NSObject (NSCoding)
-(id)initWithCoder:(NSCoder*)decoder;
-(void)encodeWithCoder:(NSCoder*)encoder;
#end
#implementation NSObject (NSCoding)
-(id)initWithCoder:(NSCoder*)decoder {
return [self init];
}
-(void)encodeWithCoder:(NSCoder*)encoder {}
#end
#implementation RSHighscore
-(id)initWithCoder:(NSCoder *)decoder {
if ((self=[super initWithCoder:decoder])) {
playerName = [[decoder decodeObjectForKey:kPlayerName] retain];
points = [decoder decodeIntegerForKey:kPoints];
}
return self;
}
...
The class you're going to encode or initWithCoder should conform to <NSCoding> protocol
So you just should add this in your interface, otherwise indeed the runtime will not recognize the selector as it's the part of <NSCoding> protocol

Error When adding Object to NSMutableArray

I am getting errors when trying to add items to a NSMutableArray which is encapsulated within an object.
Code follows:
#import <Foundation/Foundation.h>
#interface TestObject : NSObject {
NSMutableArray *myArray;
}
#property (nonatomic, retain) NSMutableArray *myArray;
#end
#import "TestObject.h"
#implementation TestObject
#synthesize myArray;
- (id) init {
if(self= [super init]){
// Initialise the Mutable Array
myArray = [[NSMutableArray alloc] init];
}
return self;
}
- (void) dealloc {
[super dealloc];
[myArray release];
}
#end
Calling:
TestObject *testObject = [[TestObject alloc] init];
NSString *someString = #"blah blah blah";
NSLog(#"%#", someString);
[testObject.myArray addObject:someString];
NSLog(#"Test Object Array Count: %#", [testObject.myArray count]);
[testObject release];
Can anyone tell me why this throws an error when calling count?
I have also tried the copy the Mutable Array to a local variable and get the same result when calling count on the local variable.
Warning warning warning!!!
[super dealloc] is the last thing you should do in your -dealloc method, not the first!
It's a good thing it just showed a warning, when I have done the same it has crashed.
The reason is that %# is an object placeholder. But the count method returns NSInteger which is a primitive datatype and the placeholder for it is %d, as you have correctly noted in the comment.

NSMutableArray for Object which has NSString property causes memory leak

I hope to add objects to a NSMutableArray "myArray", The NSMutableArray is the array for FileObj which has a NSString property "fileName"
#import <UIKit/UIKit.h>
#interface FileObj : NSObject {
NSString *fileName;
}
-(void) setfileName:(NSString *)s ;
-(NSString *) getfileName ;
#end
//
// File.m//
#import "File.h"
#implementation FileObj
-(void) setfileName:(NSString *)s ;
{
fileName=s;
}
-(NSString *) getfileName ;
{
return fileName;
}
#end
I initialize the myArray here:
NSMutableArray *temarray;
temarray=[[NSMutableArray alloc] init];
self.myArray=temarray;
[temarray release];
the codes to add object to myArray
FileObj *newobj=[[FileObj alloc]init ];
NSString *fieldValue2 = [[NSString alloc] initWithUTF8String:#"aaaa"];
[newobj setfileName:fieldValue2];
[myArray addObject:newobj];
[fieldValue2 release]; //**if I enabled the line, it will cause crash**
//**if I disable the line, it will cause memory leak**
[newobj release];
Welcome any comment
Thanks
interdev
First you should look into ObjC naming conventions. There is no -get methods in ObjC. It's also a good idea to prefix your classes with your own 2 letters (like NS).
Your setter value assignment is invalid and the NSString initialization unnecessary.
I would strongly recommend introductory material to you!
#interface MYFileObject : NSObject {
NSString *_fileName;
}
- (void)setFileName:(NSString *)theString;
- (NSString *)fileName;
#end
and the implementation
#implementation MYFileObject
- (void)setFileName:(NSString *)theString {
[_fileName release];
_fileName = [theString copy];
}
- (NSString *)fileName {
return [[_fileName copy] autorelease];
}
- (void)dealloc {
[_fileName release];
[super dealloc];
}
#end
You would add an object like this...
NSMutableArray *myAry = [[NSMutableArray alloc] init];
MYFileObject *obj = [[MYFileObject alloc] init];
[obj setFileName:#"thefilename.txt"];
[myAry addObject:obj];
[obj release];
I would recommend using properties instead of defining your own getters/setters.
You could also use the NSMutableArrays' designated initializers for fast array creation.
Look here for how to use properties: http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/ObjectiveC/Articles/ocProperties.html
Why bother with getters and setters? Use declared property already!
#interface FileObj : NSObject {
NSString *fileName;
}
#property(retain,nonatomic) NSString* fileName; // <---
#end
...
#implementation FileObj
#synthesize fileName; /// <---
-(void)dealloc {
[fileName release]; // Remember to release the object on dealloc.
[super dealloc];
}
#end
...
FileObj *newobj=[[FileObj alloc] init];
NSString *fieldValue2 = [[NSString alloc] initWithUTF8String:#"aaaa"];
newobj.fileName = fieldValue2; /// <----
[myArray addObject:newobj];
[fieldValue2 release];
[newobj release];
The crash occurs because the NSString instance is not retained anymore.
A common pattern is to retain NSString properties, either declaratively with #property or by hand.
You should modify the setter like this:
-(void) setfileName:(NSString *)s ;
{
[s retain]; // <- Retain new value
[filename release]; // <- Release old value
fileName=s;
}