In-depth analysisCocoa asynchronous requestAndLibxml2.DylibThe tutorial is the content to be introduced in this article. If you don't talk about it, you can go to the topic and see the framework on cocoachina very early. Now you have a chance to use this framework.
Here I will explain how to add this framework to the iphone project.
The procedure is as follows:
1. Download the framework: http://github.com/pokeb/asi-http-request/tree
2. Copy all the files in the root directory of the class to your project. In addition, you must copy the Reachability. h/m in External/Reachability /.
It is also copied to your project.
3. Add the required framework. See http://allseeing-i.com/ASIHTTPRequest/Setup-instructions
You need to add CFNetwork. framework, MobileCoreServices. framework, SystemConfiguration. framework, libz.1.2.3.dylib, and libxml2.dylib.
Run the project and you will find a lot of xml-related errors. Don't worry, because libxml2.dylib is the framework (this framework is not very friendly, we still need to do some work ).
In xcode, choose project> edit project settings> search "search paths" and add/usr/include/libxml2 to path.
In this way, you can learn from the official tutorials.
Http://allseeing-i.com/ASIHTTPRequest/How-to-use
I wrote a sample code XMLPerformance parsing xml, and I created a project to follow the above, but an error is prompted during compilation,
- error libxml/tree.h: No such file or directory
I immediately thought of not adding Frameworks. I added libsqlite3.dylib and libxml2.dylib, but still reported an error.
- error libxml/tree.h: No such file or directory
- An error on the .h is a compile-time error with your Header Search Paths, not a .dylib or a linker error.
- You have to ensure that /usr/include/libxml2 is in your Header Search Paths in your Release configuration。
In iphone development, asynchronous operations are an eternal topic. It is common to use asynchronous requests when iphone phones need to interact with remote servers.
Generally, this requires the combination of NSURLConnection and NSOperation. There are quite a few introductions on this network, but it is not easy to find a code that can run. Many articles are not comprehensive, or outdated sdks are used. In the new IOS version, the latest ios version is 4.2 ). These codes are classic, but they are easy to mislead people.
This article summarizes the methods and code described in many documents, and reveals implementation details in asynchronous operations and mistakes that are easy to make for beginners, including the author.
I. Use NSOperation to Implement Asynchronous requests
1. Create a new class and inherit from NSOperation.
- @ Interface URLOperation: NSOperation
- {
- NSURLRequest * _ request;
- NSURLConnection * _ connection;
- NSMutableData * _ data;
- // Construct the gb2312 encoding
- NSStringEncoding enc;
- }
- -(Id) initWithURLString :( NSString *) url;
- @ Property (readonly) NSData * data;
- @ End
The interface section is not described much. Let's look at the implementation section.
The first is a constructor with an NSString parameter. Initialize the member variable.
Here, enc is of the NSStringEncoding type. Because the characters returned by the server use Chinese characters, we use it to specify a gb2312 character encoding.
In many documents, a function called isConcurrent needs to be reloaded in NSOperation and YES needs to be returned. Otherwise, asynchronous execution is not supported. But in fact, we comment out this overload method here, and the program does not report any errors, and its execution method is still asynchronous.
- @ Implementation URLOperation
- @ Synthesize data = _ data;
- -(Id) initWithURLString :( NSString *) url {
- If (self = [self init]) {
- NSLog (@ "% @", url );
- _ Request = [[NSURLRequest alloc] initWithURL: [NSURL URLWithString: url
- // Construct the gb2312 encoding
- Enc = CFStringConvertEncodingToNSStringEncoding (kCFStringEncodingGB_18030_2000 );
- _ Data = [[NSMutableData data] retain];
- }
- Return self;
- }
- -(Void) dealloc {
- [_ Request release], _ request = nil;
- [_ Data release], _ data = nil;
- [_ Connection release], _ connection = nil;
- [Super dealloc];
- }
- // If the following function is not reloaded, an error occurs during asynchronous calling.
- //-(BOOL) isConcurrent {
- // Return YES; // If yes is returned, asynchronous call is supported; otherwise, synchronous call is supported.
- //}
The most important method in the entire class is the start method. Start is the main method of the NSOperation class. The main method name fully demonstrates its importance, because after the method is executed, the NSOperation execution thread ends the main thread that returns the caller ), at the same time, the object instance will be released, which means that other code you have defined, including the delegate method, will not be executed. In many documents, the start method only has the simplest one, including the blog post of "Easy to fly ):
- [NSURLConnection connectionWithRequest:_request delegate:self];
In this case, the delegate method has no chance of execution. Because after the start method ends, the delegate (self object) has been released, and the delegate method cannot be executed.
Therefore, in the code above, there is a while loop. The exit condition of this while loop is that the http connection is terminated and the request ends ). When the cycle ends, our work will be completed.
- // Start processing-main method of this class
- -(Void) start {
- If (! [Self isCancelled]) {
- NSLog (@ "start operation ");
- // Process events asynchronously and set a proxy
- _ Connection = [[NSURLConnection connectionWithRequest: _ request delegate: self] retain];
- // Create a loop below until the connection is terminated, so that the thread does not leave the main method. Otherwise, the connection's delegate method will not be called because the main method ends the object's lifecycle.
- // Reference http://www.cocoabuilder.com/archive/cocoa/279826-nsurlrequest-and-nsoperationqueue.html for this problem
- While (_ connection! = Nil ){
- [[Nsunloop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate distantFuture];
- }
- }
- }
Next, the NSURLConnection delegate method. The code in this part is the same as that described in most documents. You can implement all the delegate methods, however, it is sufficient to implement only three of them, and ignore the remaining methods. As you can see, you can add any code you think of in it, including receiving data, encoding characters or parsing xml.
- # Pragma mark NSURLConnection delegate Method
- // Receives incremental data)
- -(Void) connection :( NSURLConnection *) connection
- DidReceiveData :( NSData *) data {
- NSLog (@ "connection :");
- NSLog (@ "% @", [[NSString alloc] initWithData: data encoding: enc]);
- // Add data
-
- [_ Data appendData: data];
-
- }
- // When the HTTP request ends
- -(Void) connectionDidFinishLoading :( NSURLConnection *) connection {
- [_ Connection release], _ connection = nil;
- // NSLog (@ "% @", [[NSString alloc] initWithData: _ data encoding: enc]);
- }
- -(Void) connection: (NSURLConnection *) connection didFailWithError: (NSError *) error {
- NSLog (@ "connection error ");
- }
- @ End
At this point, although the Code has not been completed, we can run it. You can view the content output by the console and observe the running status of the program.
2. Call NSOperation
Our NSOperation class can be called in ViewController or directly put in AppDelegate.
Here, I click the button to trigger the call code:
- -(Void) loginClicked {
- // Construct the login request url
- NSString * url = @ "http://google.com ";
- _ Queue = [[NSOperationQueue alloc] init];
- URLOperation * operation = [[URLOperation alloc] initWithURLString: url];
- // Start processing
- [_ Queue addOperation: operation];
- [Operation release]; // The queue has its retain, which can be release;
- }
_ Queue is a NSOperationQueue object. When an NSOperation object is added to it, the NSOperation thread will be automatically executed rather than executed immediately, depending on the scheduling situation ).
3. KVO Programming Model
Our NSOperation completes the request to the server and downloads the server data to the member Variable _ data. The problem is that because all these operations are performed asynchronously, we cannot obtain the data in _ data, because we do not know when the asynchronous operation is completed, in order to access the _ data attribute, suppose we define _ data as the attribute) and obtain the server data.
We need a mechanism to notify the calling thread after NSOperation completes all work.
Here we think of the KVO programming model key-value Observation Model ). This is a design pattern used in cocoa binding technology. It enables an object to actively notify another object and trigger corresponding methods when the attribute value changes.
First, we add a BOOL variable to the NSOperation subclass. When this variable changes to YES, it indicates that the asynchronous operation has been completed:
- BOOL _isFinished;
Add the access method of this variable to the implementation:
- - (BOOL)isFinished
- {
- return _isFinished;
- }
In the KVO model of cocoa, two notification methods are available: automatic notification and manual notification. As the name suggests, automatic notification is automatically notified to the observer when the value of cocoa changes. Manual notification requires the willChangeValueForKey: And didChangeValueForKey: Method to notify the caller when the value changes. For ease of use, we generally use automatic notifications.
To use automatic notification, you must explicitly tell cocoa which key values should be used in the automaticallyNotifiesObserversForKey method:
- // Re-implement the automaticallyNotifiesObserversForKey method in the NSObject class. If yes is returned, automatic notification is returned.
- + (BOOL) :( NSString *) key
- {
- // When these two values change, the system automatically notifies the registered observer. The observer must implement observeValueForKeyPath: ofObject: change: context: method.
- If ([key isinclutostring: @ "isFinished"])
- {
- Return YES;
- }
- Return [super automaticallyNotifiesObserversForKey: key];
- }
Then, use
- [self setValue:[NSNumber numberWithBool:YES] forKey:@"isFinished"];
Instead of simply assigning values.
We need to change the isFinished value to YES in three places. When the request ends, the connection goes wrong, and the thread is cancel. Add the preceding statement to the corresponding method code.
Finally, you need to register in the observer's code. Open the place where the NSOperation subclass is called in ViewController and add:
- // Kvo Registration
- [Operation addObserver: self forKeyPath: @ "isFinished"
- Options :( NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context: operation];
- And implement the observeValueForKeyPath method:
- // Receives the Change Notification
- -(Void) observeValueForKeyPath :( NSString *) keyPath
- OfObject :( id) object
- Change :( NSDictionary *) change
- Context :( void *) context
- {
- If ([keyPath isEqual: @ "isFinished"]) {
- BOOL isFinished = [[change objectForKey: NSKeyValueChangeNewKey] intValue];
- If (isFinished) {// if the server data has been received
- [IndicatorView stopAnimating];
- URLOperation * ctx = (URLOperation *) context;
- NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding (kCFStringEncodingGB_18030_2000 );
- NSLog (@ "% @", [[NSString alloc] initWithData: [ctx data] encoding: enc]);
- // Cancel kvo Registration
- [Ctx removeObserver: self
- ForKeyPath: @ "isFinished"];
- }
- } Else {
- // Be sure to call the super implementation
- // If the superclass implements it
- [Super observeValueForKeyPath: keyPath
- OfObject: object
- Change: change
- Context: context];
- }
- }
Run the program to view the output of the console.