Vous êtes sur la page 1sur 35

XML Parsing/ Web Services

Lets understand what is a XML and what are its uses. XML is normally used in client server
architecture. Normally when client side varies so one requires standard communication between varies
client and server. XML serves the purpose for the communication.
Thus server side programmer creates XML response for the client request. Thus client side programmer
has to write XML parser to parse the XML coming inside the response from the server.
Before going into parsing XML lets first see how XML document looks like.
XML is same as HTML but XML has user define tags where as HTML has predefine tags.
When user clicks on the link http://www.gocrazygamers.com/globalscore/highScore.php?gameid=3
User gets following XML in response

<xml>
<result>
<tablerow>
<firstname>Pgfd</firstname>
<category>i</category>
<score>100</score>
</tablerow>
<tablerow>
<firstname>perks</firstname>
<category>i</category>
<score>60</score>
</tablerow>
<tablerow>

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

<firstname>Gxx</firstname>
<category>i</category>
<score>47</score>
</tablerow>

.
.
.
.
.
</result>
</xml>

The link response with players data with name, score and category.
Now we want extract these values for which we have to write a parser to parse the XML. These values
one has to store in data structure.
Lets first create a data structure to store the values, which we will extract from the XML coming from
the server.
For creating the data structure we will create class MyParserData which will extend NSObject and
will contain two NSString and one integer for storing name, category and score respectively.
For creating the class MyParserData right click on classes folder and select the new file option as shown
below


Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

After selecting the New File option you will be prompt with window to select which file one has to
create. Select Objective-C class and in subclass drop down select NSObject.

Select next and write name of the class MyParserData as shown below.


Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

The interface and implementation of the MyParserClass is as follow


#import <Foundation/Foundation.h>
@interface MyParserData : NSObject
{
NSString

*firstName;

NSString

*category;

Int

score;

}
@property (assign) NSString *firstName;
@property (readwrite) int score;
@property (assign) NSString *category;
@end
We create two NSString and one int instance variable and define them as property.
Lets see the implementation file

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

#import "MyParserData.h"
@implementation MyParserData
@synthesize firstName;
@synthesize score;
@synthesize category;
-(id)init
{
if(self = [super init])
{
firstName

NULL;

category

NULL;

score

0;

return self;
}

- (void)dealloc
{
if(firstName)
[firstName release];

if(category)
[category release];

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

[super dealloc];
}
@end

Implementation is simple where we initialize the three instance variables and release the two NSString
objects inside the dealloc method.
Now second step is to create a parser class, which will parse the XML file and store the required data
into the data structure. We will create MyParser class which will extend NSObject.
One can create MyParser class similarly MyParserData class. Now one has created the MyParser Class
lets see its interface and implementation structure.
@interface MyParser : NSObject <NSXMLParserDelegate>
{
//Variables for parsing and storing data
NSXMLParser

*myParser;

NSMutableString

*captureString;

NSMutableArray

*arrayData;

bool

flag;

MyParserData

*mydata;

}
iPhone framework provides NSXMLParser object which helps to parse the XML file. The
NSXMLParser object has to implement NSXMLParserDelegate methods to parse the XML file.
Following protocol methods we implement in the implementation file
-(void)parser:(NSXMLParser*)parserdidStartElement:(NSString*)elementName
namespaceURI:(NSString*)namespaceURIqualifiedName:(NSString*)qName attributes:(NSDictionary
*)attributeDict

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

-(void)parser:(NSXMLParser*)parserdidEndElement:(NSString*)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
-(void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
We will see the implementation of these above method later on. The foundCharacters method is called
every time once parsing called. So one has to know when to capture the data and when not to. For this
purpose we declare a Boolean variable flag. When flag is true than we capture the characters from the
XML file and when flag is false we ignore the characters.
For capturing the characters we will have to have one string object but that string object should be
mutable. Thus we declare NSMutableString. As we know XML contains chunk of data for each player.
Thus we require mutable array to store the chunk of data. Thus we declare one NSMutableArray.
Lets check the implementation file of MyParser
- (id) init
{
if(self = [super init])
{
captureString = [[NSMutableString alloc] initWithString:@""];
arrayData

= [[NSMutableArray alloc] init];

flag

= FALSE;

}
}
We initialize NSMutableString, NSMutableArray and flag to flase. We require XML file in order to
parse the file. Thus we create NSData object which will contain the XML data coming from the server.
Thus init function is modified as follows
- (id) init
{
if(self = [super init])

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

captureString = [[NSMutableString alloc] initWithString:@""];


arrayData
flag

= [[NSMutableArray alloc] init];

= FALSE;

NSData
*data = [[NSData alloc] initWithContentsOfURL:[NSURL
URLWithString:@"http://www.gocrazygamers.com/globalscore/highScore.php?gameid=3"]];

[NSThread detachNewThreadSelector:@selector(startParserWithData:) toTarget:self


withObject:data];

[data release];

}
}

NSData object is created using URL. Thus NSData object contains the XML content , which we need to
parse. Now we know parsing is time consuming process thus we send the parsing process to new thread.
Thus we create NSThread on startParserWithData which, takes NSData as argument and parse the
passed xml data.
But this way is also not the proper way as for creating NSData we ping the server and wait for data to
come till that time OS will wait. Thus we will put NSData creation also into a function in a separate
thread. Thus init function is modified as follows
- (id) init
{
if(self = [super init])

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

captureString = [[NSMutableString alloc] initWithString:@""];


arrayData

= [[NSMutableArray alloc] init];

flag

= FALSE;

[NSThread detachNewThreadSelector:@selector(startParsing) toTarget:self withObject:nil];

return self;}
Thus we make thread on startParsing which download the XML data from the server and parse the
downloaded data. The method is as shown below
-(void)startParsing
{
NSAutoreleasePool *pool

[[NSAutoreleasePool alloc] init];

if(myParser)
{
[myParser release];
myParser

nil;

myParser
=
[[NSXMLParser alloc] initWithContentsOfURL:[NSURL
URLWithString:@"http://www.gocrazygamers.com/globalscore/highScore.php?gameid=3"]];

myParser.delegate

self;


Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

if(![myParser parse])
{
//Error occured while parsing
NSLog(@"Error");
[delegate processXMLDataWithData:arrayData :PARSER_ERROR];
}

[myParser release];

[pool release];
}

Thus above method downloads the XML data and parse.

Now one question comes to my mind how main thread will come to know that parsing process is done.
As process is happening in another thread there should be some means to indicate to main thread that
parsing is done.
For establishing this communication we create our own customize protocol and declare a delegate
inside MyParser interface class.
Thus interface file of MyParser is modified as follows
#import <Foundation/Foundation.h>
#import "MyParserData.h"


Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

@protocol MyParserProtocolDelegate
- (void) processXMLDataWithData:(NSArray *) data :(int) status;
@end

@interface MyParser : NSObject <NSXMLParserDelegate>


{

//Variables for parsing and storing data


NSXMLParser

*myParser;

NSMutableString

*captureString;

NSMutableArray

*arrayData;

bool

flag;

MyParserData

*mydata;

//delegate
id <MyParserProtocolDelegate> delegate;

//property for delegate


@property (nonatomic,assign) id <MyParserProtocolDelegate> delegate;

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

- (void) startParserWithData:(NSData *) xmlData;


-(void)startParsing;

@end
We declare protocol MyParserProtocolDelegate and declare processXMLDataWithData method, which
take NSArray and int as argument.
So any class which requires XML data has to assign itself as delegate of the MyParser class and
implement processXMLDataWithData method.
Thus you can notice inside any method of MyParser class when ever processXMLDataWithData
method is called receiver is delegate and not self. Thus it means processXMLDataWithData methods
receiver will the object inside the delegate instance variable and thus controller is passed to appropriate
object.
Now we will see the implementation of NSXMLParser delegate methods.
-(void)parser:(NSXMLParser
*)parser
didStartElement:(NSString
*)elementName
namespaceURI:(NSString
*)namespaceURI
qualifiedName:(NSString
*)qName
attributes:(NSDictionary *)attributeDict
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[captureString setString:@""];
if([elementName isEqualToString:@"tablerow"])
{
flag

FALSE;

//Creat MyParserData object


mydata

[[MyParserData alloc] init];

}
else if([elementName isEqualToString:@"firstname"])

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

{
flag = TRUE;
}
else if([elementName isEqualToString:@"category"])
{
flag = TRUE;
}
else if([elementName isEqualToString:@"score"])
{
flag = TRUE;
}
else if([elementName isEqualToString:@"result"])
{
flag = FALSE;
}

[pool release];
}

didStartElement method is called when ever parser comes across any start tag. Thus we have to
compare elementName with required tag name and do the processing. So in our case we creat
MyParserData object when we encounter tablerow tag. We have to capture value between tags of
firstname, category and score thus inside if condition we make flag true where as for other tags we keep
flag as false. Thus it helps to capture values between required tags.
Now lets see didEndElement


Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

-(void)parser:(NSXMLParser
*)parser
didEndElement:(NSString
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName

*)elementName

{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

if([elementName isEqualToString:@"tablerow"])
{
//when element is closed stop flag
flag = FALSE;

[arrayData addObject:mydata];
[mydata release];

}
else if([elementName isEqualToString:@"firstname"])
{
//when firstName element is closed stop flag
flag = FALSE;

//if no data release


if(mydata.firstName != nil)
{
[mydata.firstName release];
mydata.firstName

nil;

}

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

mydata.firstName

[[NSString alloc] initWithString:captureString];

}
else if([elementName isEqualToString:@"category"])
{
flag = FALSE;
if(mydata.category != nil)
{
[mydata.category release];
mydata.category

nil;

mydata.category

[[NSString alloc] initWithString:captureString];

}
else if([elementName isEqualToString:@"score"])
{
flag = FALSE;
mydata.score =

[captureString intValue];

}
else if([elementName isEqualToString:@"result"])
{
flag = FALSE;

//results is closed


Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

if(delegate != NULL)
{
//when result is closed pass array since parsing is okay.
[delegate processXMLDataWithData:arrayData :PARSER_OK];
}
}

[pool release];
}

We need to fill elements of MyParserData object with the respective values. Thus we capture values of
respective tags and assigned them to appropriate properties of the MyParserData.
We add MyParserData object to data array and release MyParserData object. And control is passed to
delegate object when result end tag is encountered.
If error occurs during the parsing than following method is called
-(void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
//NSLog(@"Error in parser");
if(delegate != NULL)
{
[delegate processXMLDataWithData:arrayData :PARSER_ERROR];
}
}

The following method is used to capture the data of XML file



Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string


{
if(flag)
[captureString appendString:string];//

= appendString:

}
Now we have written the parser, we need to use the parser data some where. We now create one more
tab called XML and respective controller which make MyParser object to parse the XML data and
display the results in UITableView.
So we create XMLController class which extends UIViewController. Lets see interface file of
XMLController
#import <UIKit/UIKit.h>
#import "MyParser.h"
#import "MyParserData.h"

@interface XMLController : UIViewController <MyParserProtocolDelegate, UITableViewDelegate,


UITableViewDataSource>//our protocol which is declared in MyParser.h
{
MyParser

*parser;

UITableView *tb;

NSArray

*arrayTable;

//incase of customized delegate we have to declare the methods of customized protocol


- (void) processXMLDataWithData:(NSArray *) data :(int) status;

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

@end

We declare two protocol for UITableView and also one customize protocol for MyParser seen in above
code. We make MyParser object which will parse the data. UITableView to display result in table
format. NSArray to store XML data coming from the delegate method.
Thus lets see the implementation file of XMLController

-(id)init
{
if(self = [super init])
{
parser =

[[MyParser alloc] init];

parser.delegate

self;

return self;
}

- (void) processXMLDataWithData:(NSArray *) data :(int) status


{
int i=0;

switch (status)

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

{
case 0://Error
NSLog(@"Error Occured");
break;
case 1://Ok

[self loadTable];

arrayTable

[[NSArray alloc] initWithArray:data];

break;
}
}

-(void)loadTable
{
CGRect tableFrame = CGRectMake(10,10.0,300,350.0);
tb = [[UITableView alloc] initWithFrame:tableFrame style:UITableViewStyleGrouped];
tb.backgroundColor = [UIColor clearColor];
tb.dataSource = self;
tb.delegate = self;
tb.scrollEnabled = TRUE;
[self.view addSubview:tb];
}

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section


{
return [arrayTable count];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath


*)indexPath
{
return 30.0;
}
//This is the method which will be called and IS REQ since we impl interface/delegate
@end
When controller comes to XMLController class from MyParser processXMLDataWithData is called.
If Status argument is 0 it means there was some erroe so appropriate message whould be shown to user.
If status argument is 1 than we initialize local array of XMLController with the passed array and create
the UITableView.
As we know number of rows for UITableView will depend upon how many chunk of data has come
from the server. Thus number of objects inside the array will be same as number rows of UITableView.
Now we just display player name i.e. values of firstname tag in the cell of the UITableView.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:@"XMl_CELL"];

MyParserData *data =

(MyParserData*)[arrayTable objectAtIndex:indexPath.row];


Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

cell.text

data.firstName;

return cell;
}

For accomplishing this we extract the object at index provided by the indexPath. As NSArray is just
container of the object and does not know which object it holds. So one has to type cast the object.
Once we get the object we set the cells text to object firstName property. Thus run the program and
you will see the following output.

Here we have pinged the server with the GET methods. Suppose we need to send large amount of
data to the server we need to use Web services like SOAP or REST. So the MVC architect remains
same just that in webservices controller creates the request and connection and than send data return
to parser to parse. So Agent just do the parsing while controller gets the data.
In webservices only request creation ways changes else every thing remains same

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

Lets create a new controller .h and .m We will see how to create the request and get the data from
the server. After getting data one can easily create model class pass this data into the constructor
and that model object will parse data and send back the array of data back to the controller using
delegates.
#import <UIKit/UIKit.h>
@interface WebservicesDemoViewController : UIViewController
{
NSMutableData *webData;
}
-(void) createWebServiceRequest;
@end

#import "WebservicesDemoViewController.h"
@interface WebservicesDemoViewController ()
@end
@implementation WebservicesDemoViewController
- (id)init
{
self = [super init];
if (self)
{
// Custom initialization
[self createWebServiceRequest];
}
return self;
}
-(void) viewDidAppear:(BOOL)animated
{
}
-(void) createWebServiceRequest
{
NSString *soapMessage=@"<?xml version=\"1.0\" encoding=\"utf8\"?><soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"
xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap:Body><GetUser

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

List xmlns=\"http://liasesforas.com/\" /></soap:Body></soap:Envelope>";


NSURL *url = [NSURL
URLWithString:@"http://liasesforas.com/webservices/lfMobileWebService.asmx"]
;
NSMutableURLRequest *theRequest = [NSMutableURLRequest
requestWithURL:url];
NSString *msgLength = [NSString stringWithFormat:@"%d",[soapMessage
length]];
[theRequest addValue:@"text/xml" forHTTPHeaderField:@"Content-Type"];
[theRequest
addValue:@"http://liasesforas.com/webservices/lfMobileWebService.asmx/GetUse
rDetails" forHTTPHeaderField:@"GetUserDetails"];
[theRequest addValue:msgLength forHTTPHeaderField:@"Content-Length"];
[theRequest setHTTPMethod:@"POST"];
[theRequest setHTTPBody:[soapMessage
dataUsingEncoding:NSUTF8StringEncoding]];
NSURLConnection *theConnection = [[NSURLConnection alloc]
initWithRequest:theRequest delegate:self];
if(theConnection)
webData = [[NSMutableData data] retain];
else
NSLog(@"theConnection is null");
}
#pragma mark -(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 connection %@",error);
[connection release];
[webData release];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

NSLog(@"Done. received Bytes %d", [webData length]);


NSString *theXML = [[NSString alloc] initWithBytes:[webData mutableBytes]
length:[webData length] encoding:NSUTF8StringEncoding];
NSLog(@"%@",theXML);
/*
You will create the Agent which will take NSData as an argument to the
constructor and it will perform the parsing. Send processed data back to the
controller using delegate and protocols
*/
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end

We have received data from the server in the form of XML. There is one more standard format available
called JSON. There are some benefits using JSON over XML, one of the most important is that in XML
two tags are used to represent one value. Where as in JSON its Key-Value concept or it is like a
dictionary. But all platform and framework do not have in build parser for the JASON. So one have to
take some third party JSON parser. In our case we use SBJASON.
We create an agent, which takes the URL, gets the data from server send to the third party agent to parse
the data. The third party agent gives data into the big disctionary. This dictionary is send back to the
controller where it is given to the view.
We have achieved the asynchronous environment using the NSThread. There various ways through
which asynchronous can be achieved. One way is using NSOperations and NSOperationsQueue.
Lets first see NSOperation


Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

NSOperation:
In order to use the NSOperation in the code one need to follow below steps
1.

Subclass NSOperation

2.

Override main

3.

Create an autoreleasepool in main

4.

Put your code within the autoreleasepool. There reason you should create your own
autorelease pool is that you do not have access to the autorelease pool of the main thread, so you
should create your own.

We will now implement same Agent TopScoreParser but this time it will be using NSOperation. Thus
we will create a new Controller class say NSOperationDemoViewController and an Agent
TopScoreParserOperation
While creating TopScoreParserOperation we will add NSOperation inside the drop down of the
Subclass drop down while creating an agent. This agent will be responsible for downloading the data
from the server and performing the XML parsing of the top score.
The interface file of same is as follows
#import <Foundation/Foundation.h>
#import "TopScoreModel.h"
@protocol MyParserProtocolDelegate
- (void) processXMLDataWithData:(NSArray *) data :(int) status;
@end
@interface TopScoreParserOperation : NSOperation <NSXMLParserDelegate>
{
//Variables for parsing and storing data
NSXMLParser
*myParser;
NSMutableString
*captureString;
NSMutableArray
*arrayData;
bool

flag;

TopScoreModel

*player;

}
@property (nonatomic,assign) id <MyParserProtocolDelegate> delegate;
@property (assign, atomic) NSMutableArray
*arrayData;
@end

NSOperation class needs to implement the its own main method. Thus code which we have written in

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

startParsing method will be copied inside the main methods as shown below
- (void)main
{
NSAutoreleasePool *pool =

[[NSAutoreleasePool alloc] init];

captureString = [[NSMutableString alloc] initWithString:@""];


arrayData = [[NSMutableArray alloc] init];
flag = FALSE;
if(myParser)
{
[myParser release];
myParser
=
nil;
}
myParser
=
[[NSXMLParser alloc] initWithContentsOfURL:[NSURL
URLWithString:@"http://www.gocrazygamers.com/globalscore/highScore.php?gamei
d=3"]];
myParser.delegate

self;

if(![myParser parse])
{
NSLog(@"Error While Parsing");
}

[myParser release];
[pool release];
}

The remaining code for XML parsing remains the same. Now comes how will main thread will come to
about the completion of the task by the new thread. In NSThread we used Protocols and delegate. In this
case also we can use Protocol and Delegate. But we will also find some more alternatives
In the alternative we have to use blocks. So before we go into the alternate methods lets spend some
time in understanding the blocks in the objective C. The concept of block is simple but it takes time to
understand. So please be mechanical while using block if you have problem understanding the block
concepts.


Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

Block
Beginning with iOS 4.0, Apple introduced blocks, which look and operate much like C functions. A
block is really nothing more than a chunk of code. What makes them unique is that a block can be
executed inline as well as passed as an argument into a method/function. Blocks can also be assigned to a
variable and called as you would call a C function.
To define a block variable, the ^ operator is used.
Lets create a simple block that will return BOOL value (YES/NO) based on whether the passed in
integer or passed argument to block is an even or odd value
BOOL (^isInputEven)(int) = ^(int input)
{
if (input % 2 == 0)
return YES;
else
return NO;
};

The return type of the block is BOOL. isInputEven is the variable we declared as block. The block
accepts one argument of type int.
The argument, which will be pass to the block locally inside the block, will be referred as input. The code
with if & else is the body of the block.
Lets declare an int variable -101 and pass to the above declared block and depending upon the return
value we will display in Log whether its an even or odd number.
int x = -101;
NSLog(@"%d %@ number", x, isInputEven(x) ? @"is an even" : @"is not an
even");

This looks simple now lets see how block treats the local variables inside the block body. So we declare a
price variable outside the block and use it inside the block as shown below
float price = 1.99;
float (^finalPrice)(int) = ^(int quantity)
{
// Notice local variable price is
// accessible in the block
return quantity * price;
};

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

int orderQuantity = 10;


NSLog(@"Ordering %d units, final price is: %2.2f", orderQuantity,
finalPrice(orderQuantity));
The output for the log statement is: Ordering 10 units, final price is: 19.90
Lets change the price and run the block again and see the ouput
price = .99;
NSLog(@"Ordering %d units, final price is: %2.2f", orderQuantity,
finalPrice(orderQuantity));
The output for the log statement is: Ordering 10 units, final price is: 19.90
You have notice the output didnt change as price value didnt change this is because during the block
definition the price variable is set to a const, so the output remains the same as before.
In order to allow a variable defined outside a block to be mutable, apply the __block storage type
modifier. (please note there are two underscore before word block)
// Use the __block storage modifier to allow changes to 'price'
__block float price = 1.99;
float (^finalPrice)(int) = ^(int quantity)
{
return quantity * price;
};
int orderQuantity = 10;
price = .99;
NSLog(@"With block storage modifier - Ordering %d units, final price
is: %2.2f", orderQuantity, finalPrice(orderQuantity));
Out put is With block storage modifier Ordering 10 units, final price is: 9.90
At some point you might have the same signature for two distinct blocks. This is where the typdef comes
in a best practice is to use typedef if the same block signature is used for two or more unique
operations.
For example, below a typedef is defined for a block that accepts two objects and returns an
NSComparisonResult:
typedef NSComparisonResult(^CompareObjects)(id, id);

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

NSComparisonResult is an enum that can be used to indicate the ordering of


items (ascending, descending, same).
With the typedef in place you could now write two blocks, both accepting objects, however each
performing a different comparison:
CompareObjects compareAccountBalance = ^(id objectA, id objectB)
{
// Do comparisons here...
// return NSComparisonResult value (NSOrderedAscending,
NSOrderedSame, NSOrderedDescending)
};
Here is another block with the same signature, however, there you could change up the logic for
comparing the objects:
CompareObjects compareAccountActivity = ^(id objectA, id objectB)
{
// Do comparisons here...
// return NSComparisonResult value (NSOrderedAscending, NSOrderedSame,
NSOrderedDescending)
};
One of the most important feature of the block is enumeration on collection type objects. With iOS 4.0 all
collection type objects has enumeration using blocks as a method added. Lets see w.r.t NSArray
We will first create NSArray holding some NSString variables
// Create an array
NSArray *array = [NSArray arrayWithObjects: @"12345", @"12345678",
@"abcd", @"123abc", nil];
[array enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop)
{
NSLog(@"Array entry %d contains: %@", idx, [array
objectAtIndex:idx]);
}];
Out put will be
Array entry 1 contains: 12345
Array entry 2 contains: 12345678
Array entry 3 contains: abcd
Array entry 4 contains: 123abc

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

If you have noticed block takes two arguments one index other one Boolean pointer to stop the
enumeration.
Also there is one more interesting method introduce in NSArray called indexOfObjectPassingTest
which takes block as an argument.
In order to demonstrate lets take more complex data structure. We have array of dictionary. We need to
stop the enumeration once we get particular value from the dictionary.
NSArray *arrayOfDictionaryObjects = [NSArray arrayWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:@"1234", @"accountID",
[NSNumber numberWithBool:NO], @"isActive", nil],
[NSDictionary dictionaryWithObjectsAndKeys:@"2345", @"accountID",
[NSNumber numberWithBool:NO], @"isActive", nil],
[NSDictionary dictionaryWithObjectsAndKeys:@"3456", @"accountID",
[NSNumber numberWithBool:YES], @"isActive", nil],
[NSDictionary dictionaryWithObjectsAndKeys:@"4567",
@"accountID", [NSNumber numberWithBool:NO], @"isActive", nil],
nil];
The above array has three dictionaries and each dictionary holds account id and a Boolean to check
whether account is active or not.
We will stop enumeration as soon we get the an active acaount so we will enumerate the array using
blocks is shown below
NSUInteger activeAccount = [arrayOfDictionaryObjects
indexOfObjectPassingTest: ^(id obj, NSUInteger idx, BOOL *stop)
{
NSNumber *num = [obj valueForKey:@"isActive"];
if ([num boolValue] == YES)
{
// Stop processing the block
*stop = YES;
return YES;
}
else
return NO;
}];


Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

Lets look at one more example using the same dictionary that was defined above, where one value in
the dictionary is the account number and one indicates if the account is active:
To print the active accounts from the array of dictionaries, (notice for this example both the 1st and 3rd
entries are set as active), we can enumerate the array using a block and check the value of the
isActive key in the dictionary:
[arrayOfDictionaryObjects enumerateObjectsUsingBlock:^(id object,
NSUInteger index, BOOL *stop)
{
// Print the active accounts:
if ([object valueForKey:@"isActive"] == [NSNumber
numberWithBool:YES])
NSLog(@"Active account: %@", [object valueForKey:@"accountID"]);
}];

Now as we are familiar with the blocks lets check the alternative approach.
One approach with the NSOperation is use of completion handler. Completion handler takes block as an
argument. This completion handler is called when NSOperation has done the task or one can say main
function has completed the execution. So normally we use the NSoperation properties inside the
completion handler block and assign to local variables of the controller and call the UI related
operations.
Thus in the controller i.e. NSOperationDemoViewController init method where I create NSOperation
object we also set the completionHandler method
- (id)init
{
self = [super init];
if (self)
{
// Custom initialization
topscoreOperation

[[TopScoreParserOperation alloc] init];

[topscoreOperation setCompletionBlock:^{
NSLog(@"Count %d",topscoreOperation.arrayData.count);
arrayTable =
[[NSArray alloc]
initWithArray:topscoreOperation.arrayData];
[self loadTable];
}];

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

[topscoreOperation start];
}
return self;
}

When we used delegates and protocols we called startParsing method. But in NSOperation one need to
call start method and automatically main method, which is implemented inside the NSOperation
subclass is called and process starts.
But the completion handler is called when sub class of NSOperation finishes the task i.e. main function.
What if agent needs to update main thread in middle than the approach will through delegate. One
should make sure its always called on the main thread we can use following NSOperationQueue with
block methods
[[NSOperationQueue mainQueue] addOperationWithBlock: ^
{
[delegate processXMLDataWithData:arrayData :1];
}];

There many more methods which, one can use with NSOperation they are as follows
You can make an operation dependent on other operations. Any operation can be dependent on any
number of operations. When you make operation A dependent on operation B, even though you call
start on operation A, it will not start unless operation B isFinished is true. For example:
TopScoreParserOperation *topscoreOpt = [[TopScoreParserOperation alloc] init]; //

MyDownloadOperation is a subclass of NSOperation


CableFinder *cablefinderOp = [[CableFinder alloc] init]; // MyFilterOperation is a subclass of
NSOperation
[cablefinderOp addDependency: topscoreOpt];
To remove dependencies:
[cablefinderOp removeDependency: topscoreOpt];
sometimes the operation you wish to run in the background is not crucial and can be performed at a
lower priority. You set the priority of an operation by using setQueuePriority:
[cablefinderOp setQueuePriority:NSOperationQueuePriorityVeryLow];
Other options for thread priority are: NSOperationQueuePriorityLow,
NSOperationQueuePriorityNormal, NSOperationQueuePriorityHigh, and
NSOperationQueuePriorityVeryHigh.

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

When you add operations to a queue, the NSOperationQueue looks through all of the operations, before
calling start on them. Those that have higher priorities will be executed first. Operations with the
same priority will be executed in order of submission to the queue (FIFO).
Lets see what is NSOperationQueue
NSOperationQueue
NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
myQueue.name = @"Download Queue";
A queue is not the same thing as thread. A queue can have multiple threads. Each operation within a
queue is running on its own thread. Take the example where you create one queue, and add three
operations to it. The queue will launch three separate threads, and run all operations concurrently on
their own threads.
How many threads will be created? Thats a good question! It depends on the hardware. By default,
NSOperationQueue class will do some magic behind the scenes, decide what is best for the particular
platform the code is running on, and will launch the maximum possible number of threads.
Consider the following example. Assume the system is idle, and there are lots of resources available, so
NSOperationQueue could launch something like eight simultaneous threads. Next time you run the
program, the system could be busy with other unrelated operations, which are consuming resources, and
NSOperationQueue will only launch two simultaneous threads.
You can set the maximum number of operations that NSOperationQueue can run concurrently.
NSOperationQueue may choose to run any number of concurrent operations, but it wont be more than
the maximum.
myQueue.MaxConcurrentOperationCount = 3;
If you change your mind, and want to set MaxConcurrentOperationCount back to its default, you would
perform the following changes:
myQueue.MaxConcurrentOperationCount =
NSOperationQueueDefaultMaxConcurrentOperationCount;
As soon as an operation is added to a queue, you should relinquish ownership by sending a release
message to the operation object (if using manual reference counting, no ARC), and the queue will then
assume responsibility to start the operation. At this point, it is up to the queue as to when it will call
start.
[myQueue addOperation:downloadOp];
[downloadOp release]; // manual reference counting
At any time you can ask a queue which operations are in the queue, and how many operations there are

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

in total. Remember that only those operations that are waiting to be executed, and those that are
running, are kept in the queue. As soon as an operation is done, it is gone from the queue.
NSArray *active_and_pending_operations = myQueue.operations;
NSInteger count_of_operations = myQueue.operationCount;
You can pause a queue by setting setSuspended:YES. This will suspend all operations in a queue
you cant suspend operations individually. To resume the queue, simply setSuspended:NO.

//Suspend a queue
[myQueue setSuspended:YES];
.
.
.
// Resume a queue
[myQueue setSuspended: NO];
To cancel all operations in a queue, you simply call cancelAllOperations.
The reason is that cancelAllOperations doesnt do much, except that it calls cancel on every
operation in the queue it doesnt do anything magical! If an operation has not yet started, and you
call cancel on it, the operation will be cancelled and removed from the queue. However, if an
operation is already executing, it is up to that individual operation to recognize the cancellation (by
checking the isCancelled property) and stop what it is doing.
[myQueue cancelAllOperations];
If you have a simple operation that does not need to be subclassed, you can simply pass it into a queue
by way of a block. If you want to send any data back from the block, remember that you should not pass
into the block any strong reference pointers; instead, you must use a weak reference. Also, if you want
to do something that is related to the UI in the block, you must do it on the main thread:
UIImage *myImage = nil;
// Create a weak reference
__weak UIImage *myImage_weak = myImage;
// Add an operation as a block to a queue
[myQueue addOperationWithBlock: ^ {

// a block of operation
NSURL *aURL = [NSURL
URLWithString:@"http://www.somewhere.com/image.png"];
NSError *error = nil;
NSData *data = [NSData dataWithContentsOfURL:aURL options:nil

Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.


Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

error:&error];
If (!error)
[myImage_weak imageWithData:data];
// Get hold of main queue (main thread)
[[NSOperationQueue mainQueue] addOperationWithBlock: ^ {
myImageView.image = myImage_weak; // updating UI
}];
}];


Nimap Infotech, B-204, Pawapuri Apts, Love Lane, Mumbai 400010.
Tel.:- +91 986 935 7889 Email:- info@nimapinfotech.com Web:- www.nimapinfotech.com

Vous aimerez peut-être aussi