About how to use NSThread - iphone

I'm an iPhone developer.
I studied about how to use NSThread. So I created source code.
But, I'm not sure about my source code whether good or bad.
-(void)check_threadEnd
{
if ([_thread isFinished]) {
threadCount++;
if (threadCount == 4) {
[self performSelector:#selector(removeActivityView) withObject:nil afterDelay:0.0];
[self.tableView reloadData];
}
}
}
Sometimes, threadCount doesn't become 4.
So, ActiveView is worked continual without stopping.
Turn the timer after a period of time, remove ActiveView?
I'll give you some advice please.
-(IBAction)click_ServerSync:(id)sender
{
if ([util checkNetwork]) {
threadCount = 0 ;
[self displayActivityView];
NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
[queue setMaxConcurrentOperationCount:4];
_thread = [[NSThread alloc] initWithTarget:self selector:#selector(_th) object:nil];
[_thread start];
}
}
-(void)_th
{
[self performSelectorOnMainThread:#selector(LoadXml:) withObject:#"XML1" waitUntilDone:NO];
[self performSelectorOnMainThread:#selector(LoadXml:) withObject:#"XML2" waitUntilDone:NO];
[self performSelectorOnMainThread:#selector(LoadXml:) withObject:#"XML3" waitUntilDone:NO];
[self performSelectorOnMainThread:#selector(LoadXml:) withObject:#"XML4" waitUntilDone:NO];
}
-(void)LoadXml:(NSString*)P_VAL
{
NSString *smsURL = [NSString stringWithFormat:#"%#%#.asp", XML_URL, P_VAL];
NSString *sendAuthInfo = [NSString stringWithFormat:#"A=%#&B=%d&C=%#&D=%#" , A, B, C, D ];
NSString *val = [sendAuthInfo stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc]initWithURL:[NSURL URLWithString:smsURL]]autorelease];
[request setURL:[NSURL URLWithString:smsURL]];
[request setHTTPMethod:#"POST"];
[request setHTTPBody: [val dataUsingEncoding: NSUTF8StringEncoding]];
[self startAsyncLoad:request tag:P_VAL];
}
- (void)startAsyncLoad:(NSMutableURLRequest*)request tag:(NSString*)tag {
CustomURLConnection *connection = [[CustomURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES tag:tag];
if (connection) {
[receivedData setObject:[[NSMutableData data] retain] forKey:connection.tag];
}
}
- (NSMutableData*)dataForConnection:(CustomURLConnection*)connection {
NSMutableData *data = [receivedData objectForKey:connection.tag];
return data;
}
-(void)check_threadEnd
{
if ([_thread isFinished]) {
threadCount++;
if (threadCount == 4) {
[self performSelector:#selector(removeActivityView) withObject:nil afterDelay:0.0];
[self.tableView reloadData];
}
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSMutableData *dataForConnection = [self dataForConnection:(CustomURLConnection*)connection];
[dataForConnection setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSMutableData *dataForConnection = [self dataForConnection:(CustomURLConnection*)connection];
[dataForConnection appendData:data];
}
-(void)connectionDidFinishLoading:(NSURLConnection*)connection
{
NSLog(#" connection connectionDidFinishLoading : %d", [connection retainCount]);
NSMutableData *dataForConnection = [self dataForConnection:(CustomURLConnection*)connection];
[connection release];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSXMLParser *xmlParser = [[[NSXMLParser alloc] initWithData:dataForConnection] autorelease];
XMLParser *parser = [[XMLParser alloc] initXMLParser];
[xmlParser setDelegate:(id)parser];
parser.viewDelegate = (id)self;
[xmlParser parse]; // xml parser
}

Do you have a reason as to why you are opting for NSThread over NSOperation? NSOperation abstracts away a lot of the lower-level thing you would have to worry about with NSThread. I would strongly recommend you read up on this concurrency programming guide from Apple.
Pay attention to the last section, Migrating Away From Threads, as it talks about additional alternatives that can help you write robust, clean code.

Related

cfstring length message sent to deallocated instance?

I m doing the concept of autocomplete feature for a textField and in doing so I m getting the following error:
"cfstring length message sent to deallocated instance"
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if( textField == txtcity)
{
autocompleteTableView.hidden = NO;
NSString *substring = [NSString stringWithString:textField.text];
substring = [substring stringByReplacingCharactersInRange:range withString:string];
[self searchAutocompleteEntriesWithSubstring:substring];
return YES;
}
}
- (void)searchAutocompleteEntriesWithSubstring:(NSString *)substring {
subString2=[NSString stringWithFormat:#"%#",substring];
NSMutableData *data = [[NSMutableData alloc] init];
self.receivedData = data;
[data release];
NSURL *jsonUrl =[NSURL URLWithString:[NSString stringWithFormat:#"http://210.90.32.122/services/AutoService.svc/GetCities/?p=%#&k=%#",substring,txtId.text]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:jsonUrl];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
self.connection = connection;
[connection start];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
parser = [[NSXMLParser alloc] initWithData:receivedData];
[parser setDelegate:self];
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
[parser parse];
[parser release];
NSLog(#"%#",arr2);
if([arr2 count]!=0)
{
self.autocompleteUrls = [[NSMutableArray alloc] init];
if(autocompleteTableView)
[autocompleteTableView removeFromSuperview];
autocompleteTableView = [[UITableView alloc] initWithFrame:CGRectMake(88, 428, 200,[arr2 count]*20) style:UITableViewStyleGrouped];
autocompleteTableView.delegate = self;
autocompleteTableView.dataSource = self;
autocompleteTableView.scrollEnabled = YES;
autocompleteTableView.rowHeight=20;
[self.view addSubview:autocompleteTableView];
for(int i=0;i<[arr2 count];i++)
{
NSString *curString = [[arr2 objectAtIndex:i] valueForKey:#"Name"];
NSRange substringRange = [curString rangeOfString:subString2]; //error at this line
if (substringRange.location == 0)
[autocompleteUrls addObject:curString];
}
}
[autocompleteTableView reloadData];
txtcity.text=subString2;
[txtcity resignFirstResponder];
}
my subString2 is becoming null.But I have retained it while decalring
#property(nonatomic,retain)NSString *subString2;
Where Im going wrong.Couldn't understand where it is being deallocated..
You're not actually retain it. You're setting the object variable subString2 not though the property, that is the reason. Try this:
self.subString2 = [NSString stringWithFormat:#"%#",substring];
But it is not very good practise to get to the object variables from the object through the properties. There is two options:
[subString2 release];
subString2 = [[NSString stringWithFormat:#"%#",substring] copy];
OR
[subString2 release];
subString2 = [[NSString stringWithFormat:#"%#",substring] retain];

iOS 5 NSURLConnection with NSOperationQueue - Providing UI Feedback

I need to make multiple NSURLConnections to a JSON Web Service. I would like each WS call to keep in UI informed, probably with a UIActivityIndicatorView and label. So far I've created a NSURLConnection helper class to handle the connection and placed the URL delegates in the View. This works great for updating the UI with a single WS call.
For multiple calls, I'm trying to use an NSOperationQueue. I'd like to setMaxConcurrentOperationCount to one on the queue so that each Operation executes one at a time. Here's the relevant code on my View Controller:
ViewController.m
#import "URLOperationHelper.h"
#implementation ViewController
- (IBAction)showPopup:(id)sender
{
// Dictonary holds POST values
NSMutableDictionary *reqDic = [NSMutableDictionary dictionary];
// Populate POST key/value pairs
[reqDic setObject:#"pw" forKey:#"Password"];
[reqDic setObject:#"ur" forKey:#"UserName"];
operationQueue = [[NSOperationQueue alloc] init];
[operationQueue setMaxConcurrentOperationCount:1];
[operationQueue cancelAllOperations];
[operationQueue setSuspended:YES];
URLOperationHelper *wsCall1 = [[URLOperationHelper alloc] initWithURL:#"urlString1" postParameters:reqDic urlDelegate:self];
URLOperationHelper *wsCall2 = [[URLOperationHelper alloc] initWithURL:#"urlString2" postParameters:reqDic urlDelegate:self];
[operationQueue addOperation:wsCall1];
[operationQueue addOperation:wsCall2];
}
// Did the URL Connection receive a response
-(void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"Did receive response: %#", response);
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
int code = [httpResponse statusCode];
// Handle status code here
webData = [[NSMutableData alloc]init];
}
// Did the URL Connection receive data
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"Did receive data: %#", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
assert(webData != nil);
[webData appendData:data];
}
// Did the connection fail with an error?
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"%#", error);
}
// Executes after a successful connection and data download
-(void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Connection finished");
}
#end
And here is my URLOperationHelper.m
#implementation URLHelper
- (id)initWithURL:(NSString *)urlPath
postParameters:(NSMutableDictionary *)postParameters
urlParentDelegate:(id) pDelegate
{
if(self = [super init])
{
connectionURL = urlPath;
postParams = postParameters;
parentDelegate = pDelegate;
}
return self;
}
- (void)done
{
// Cancel the connection if present
if(urlConnection)
{
[urlConnection cancel];
urlConnection = nil;
}
// Alert
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
executing = NO;
finished = YES;
[self willChangeValueForKey:#"isFinished"];
[self willChangeValueForKey:#"isExecuting"];
}
- (void)cancel
{
// Possibly add an NSError Property
[self done];
}
- (void)start
{
// Make sure this operation starts on the main thread
if(![NSThread isMainThread])
{
[self performSelectorOnMainThread:#selector(start) withObject:nil waitUntilDone:NO];
return;
}
// Make sure that the operation executes
if(finished || [self isCancelled])
{
[self done];
return;
}
[self willChangeValueForKey:#"isExecuting"];
executing = YES;
[self main];
[self willChangeValueForKey:#"isExecuting"];
}
- (void)main
{
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:postParams options:NSJSONWritingPrettyPrinted error:&error];
// Convert dictionary to JSON
NSString *requestJSON = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(#"JSONRequest: %#", requestJSON);
// Declare Webservice URL, request, and return data
url = [[NSURL alloc] initWithString:connectionURL];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
NSData *requestData = [NSData dataWithBytes:[requestJSON UTF8String] length:[requestJSON length]];
// Build the request
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"%d", [requestData length]] forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:requestData];
// Connect to Webservice
// Responses are handled in the delegates below
urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:parentDelegate startImmediately:YES];
}
- (BOOL)isConcurrent
{
return YES;
}
- (BOOL)isExecuting
{
return executing;
}
-(BOOL)isFinished
{
return finished;
}
#end
The problem that I'm having is the Start method for the URLOperation is never called. The OperationQueue is created and the Operations are called, but nothing happens after that, execution or thread wise.
Also, is this a correct line of thinking to provide UI feedback using NSOperationQueues like this? I.E. calling the NSURLDelegates from the Operation?
If you set setSuspended to YES before adding the Operations then your Operations will be queued into a suspended queue.. i suggest not to suspend the queue at
furthermore, your operation never ends anyway. You need to assign the operation itself as the delegate and implement all necessary delegate methods. In these methods you can forward the messages to your parentDelegate and decide when you are finished and call your done method when appropriate (i suggest connection:didFailWithError: and connectionDidFinishLoading:)
There is a good tutorial here: http://blog.9mmedia.com/?p=549
You are also not completely implementing key-value-coding compilant properties correct. Whenever you call willChangeValueForKey: you also need to call didChangeValueForKey afterwards:
- (void)start
{
...
[self willChangeValueForKey:#"isExecuting"];
executing = YES;
[self didChangeValueForKey:#"isExecuting"];
[self main];
}
and:
- (void)done
{
...
// Alert
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
executing = NO;
finished = YES;
[self didChangeValueForKey:#"isFinished"];
[self didChangeValueForKey:#"isExecuting"];
}
See this Q/A for KVC: when to use "willChangeValueForKey" and "didChangeValueForKey"?

Asynchronous connection not getting called

I want to create two connections in a single view controller class. I am using NSOperationQueue for this purpose. The two connections are created in two functions and push inside the queue .The problem is delegates are not called. Please help me out. Thanks you in advance
- (void)viewDidLoad {
[super viewDidLoad];
NSOperationQueue *queue=[[NSOperationQueue alloc] init];
NSInvocationOperation *invOperation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(createConnection1) object:nil];
NSInvocationOperation *invOperation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(createConnection2) object:nil];
NSArray *ops=[[NSArray alloc] initWithObjects:invOperation1,invOperation2,nil];
[queue addOperations:ops waitUntilFinished:YES];
}
-(void) createConnection1{
//create connection
NSLog(#"Create Connection 1");
url1 =[[NSMutableString alloc] initWithFormat:#"http://www.google.com/ig/api?weather=New Delhi"];
NSURLRequest *theRequest1=[NSURLRequest requestWithURL:[NSURL URLWithString:url1]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
theConnection1=[[NSURLConnection alloc] initWithRequest:theRequest1 delegate:self];
if (theConnection1) {
connectionCreated1=YES;
receivedData1 = [[NSMutableData data] retain];
NSLog(#"received data 1 %#",receivedData1);
//[theConnection1 setDelegate:self];
}
}
-(void) createConnection2{
//create connection
NSLog(#"Create Connection 2");
url2 =[[NSMutableString alloc] initWithFormat:#"http://www.google.com/ig/api?weather=Chennai"];
NSURLRequest *theRequest2=[NSURLRequest requestWithURL:[NSURL URLWithString:url2]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
theConnection2=[[NSURLConnection alloc] initWithRequest:theRequest2 delegate:self];
if (theConnection2) {
connectionCreated2=YES;
receivedData2 = [[NSMutableData data] retain];
//[theConnection2 setDelegate:self];
NSLog(#"received data 2 %#",receivedData2);
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
if (connectionCreated1==YES) {
[receivedData1 setLength:0];
}
else if (connectionCreated2==YES) {
[receivedData2 setLength:0];
}
else {
NSLog(#"did not receive response");
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
if (connectionCreated1==YES) {
[theConnection1 release];
xmlParser1 = [[NSXMLParser alloc] initWithData:receivedData1];
[xmlParser1 setDelegate:self];
[xmlParser1 parse];
}
else if(connectionCreated2==YES){
[theConnection2 release];
xmlParser2 = [[NSXMLParser alloc] initWithData:receivedData2];
[xmlParser2 setDelegate:self];
[xmlParser2 parse];
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"connection failed" message:#"" delegate:self cancelButtonTitle:#"ok" otherButtonTitles:nil];
[alert show];
[alert release];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
if (connectionCreated1==YES) {
[receivedData1 appendData:data];
}
else if(connectionCreated2==YES) {
[receivedData2 appendData:data];
}
else {
NSLog(#"data not received");
}
}
The URL you have given in the first link seems to be borken ... look at this "http://www.google.com/ig/api?weather=New Delhi".. there is space between new delhi. try this instead http://www.google.com/ig/api?weather=New+Delhi

Zombie kills my App

I'm implementing asynchronous image loading in UITableView, If I scroll rows fast my app crashes due to message sent to zombie... What is wrong am I doing here?
//loading image from URL
-(void)loadImageFromURL:(NSURL*)url {
if (connection!=nil) { [connection release]; }
//data is NSMutableData
if (data!=nil) { [data release]; }
NSURLRequest* request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
//Append received data when it is received
- (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)incrementalData {
if (data==nil) { data = [[NSMutableData alloc] init]; }
[data appendData:incrementalData]; //Message sent to zombie, app CRASHES HERE
}
//When finished
- (void)connectionDidFinishLoading:(NSURLConnection*)theConnection {
//so self data now has the complete image
[connection release];
connection=nil;
//Use received data to construct image
[data release];
data=nil;
}
Here:
if (connection!=nil) { [connection release]; }
if (data!=nil) { [data release]; }
you are releasing the data. Later you try using the released data so it crashes. Try this:
if (connection!=nil) { [connection release]; connection = nil; }
if (data!=nil) { [data release]; data = nil; }
That way your if statements will actually trigger.

How to parse Google weather API using NSXML?

i want to parse google weather API using NSXML so please give me some Guidance for this.
This is My url
and i have taken such kind of steps:
NSURL *url = [NSURL URLWithString:#"http://www.google.com/ig/api?weather=Ahemdabad"];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];
[theRequest setHTTPMethod:#"POST"];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if(theConnection){
webData = [[NSMutableData data] retain];
NSLog( #"connection established");
}
else {
NSLog(#"theConnection is NULL");
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[webData setLength: 0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[webData appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"ERROR with theConenction");
[connection release];
[webData release];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connectio
{
NSLog(#"DONE. Received Bytes: %d", [webData length]);
NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];
NSLog(#"thexml=============>%#", theXML);
[theXML release];
if(parser)
{
[parser release];
}
parser = [[NSXMLParser alloc]initWithData:webData];
[parser setDelegate: self];
[parser setShouldResolveExternalEntities: YES];
[parser parse];
[connection release];
[webData release];
}
hey ankit you can get this code if at all its helpful to you no need to establish connection
just use this method
-(id)initWithURL:(NSURL*)url arrayRootObjectTags:(NSArray*)arrTags sel:(SEL)seletor andHandler:(NSObject*)handler{
if(self = [super init] ){
self.mainArray=arrTags;
self.MainHandler=handler;
self.targetSelector=seletor;
NSLog(#"%#",[url description]);
NSURLRequest *req=[NSURLRequest requestWithURL:url cachePolicy:NSURLCacheStorageNotAllowed timeoutInterval:30];
con=[[NSURLConnection alloc] initWithRequest:req delegate:self];
if(con){
myWebData=[[NSMutableData data] retain];
} else {
[MainHandler performSelector:#selector(targetSelector:) withObject:nil];
}
}
return self;
}
also the other supporting method
-(void)parser:(NSXMLParser*)parser didStartElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName attributes:(NSDictionary*)attributeDict {
if([elementName isEqualToString:#"html"] || [elementName isEqualToString:#"HTML"]){
didGetHTML=YES; [self parserDidEndDocument:parser];
} else if([[mainArray objectAtIndex:0] isEqualToString:elementName] && [[mainArray objectAtIndex:1] isEqualToString:elementName] && !didGetHTML) {
objectsArray=[[NSMutableArray alloc] init];
tmpD=[[NSMutableDictionary alloc] init];
if(tmpOther==nil) tmpOther=[[NSMutableDictionary alloc] init];
} else if([[mainArray objectAtIndex:0] isEqualToString:elementName] && !didGetHTML ) {
objectsArray=[[NSMutableArray alloc] init];
if(tmpOther==nil) tmpOther=[[NSMutableDictionary alloc] init];
} else if([[mainArray objectAtIndex:1] isEqualToString:elementName] && !didGetHTML ) {
tmpD=[[NSMutableDictionary alloc] init];
} else if([mainArray containsObject:elementName] && !didGetHTML){
[tmpD setValue:[attributeDict valueForKey:#"data"] forKey:elementName];
}
}
-(void)parser:(NSXMLParser*)parser foundCharacters:(NSString*)string {
if(tmpString==nil && !didGetHTML){
tmpString=[[NSString alloc] initWithString:string];
} else if(!didGetHTML){
NSString *t=[NSString stringWithString:tmpString];
if([tmpString retainCount]>0) { [tmpString release]; tmpString=nil; }
tmpString=[[NSString alloc] initWithFormat:#"%#%#",t,string];
}
}
-(void)parser:(NSXMLParser*)parser didEndElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName {
if([[mainArray objectAtIndex:0] isEqualToString:elementName] && [[mainArray objectAtIndex:1] isEqualToString:elementName] && !didGetHTML){
[objectsArray addObject:tmpD];
} else if([elementName isEqualToString:[mainArray objectAtIndex:1]] && !didGetHTML){
[objectsArray addObject:tmpD];
[tmpD release]; tmpD=nil;
} else if([mainArray containsObject:elementName] && !didGetHTML) {
if(![tmpD valueForKey:elementName]){
[tmpD setValue:tmpString forKey:elementName];
}
[tmpString release]; tmpString=nil;
} else {
[tmpOther setValue:tmpString forKey:elementName];
[tmpString release]; tmpString=nil;
}
}
and simply call the initwith url method from which ever class you have written this method
just you have to give root tag object tag and element tag of a particular object tag and also give selector after that take the response in dictionary and they take it in array and display the result according to your value for key