I'm trying to get UIProgressView, NSURLConnection, NSOperation and NSOperationQueue working together.
Here is the code: http://pastie.org/4080576
Problem:
connection:DidReceiveData: never called: execution going immediately to -start(), through -main(), then to -connection:DidReceiveResponse:, back to -main() (sometimes), and finally to -connectionDidFinishDownloading:destinationURL:.
When I trying to download picture from this link:
http://upload.wikimedia.org/wikipedia/commons/4/41/Kharkov_picture_1787.jpg
I get this output:
2012-06-13 19:43:06.189 downloadingFilesInOpQueue[5070:f803] Received
response: 2012-06-13 19:43:06.190
downloadingFilesInOpQueue[5070:f803] Filesize is: 3075638 Suggested
filename is: Kharkov_picture_1787.jpg 2012-06-13 19:43:12.476
downloadingFilesInOpQueue[5070:f803] Finished downloading. Received 0
bytes
Also, I can't figure out where connection:didReceiveResponse: belong:
NSURLConnectionDelegate or NSURLConnectionDataDelegate.
P.S. If there some style issues, I would glad to hear about them. Thx.
I strongly suspect your "busy" while-loops are consuming the underlying run-loop's execution and choking out the NSURLConnection from being able to handle data as it arrives.
Looking at your pastie.org code, here's your primary culprits:
while (![self isFinished]) {
...
while ([self fileSize] == 0) {}
while ([self receivedDataLength] == 0) {}
...
}
Busy loops like this should almost never be used in iOS. iOS programming is largely event-driven, especially NSURLConnection. Instead, update your UIProgressView in response to connection:DidReceiveData: etc.
Honestly, coding your own NSURLConnection handlers is kind of a messy pain. I strongly recommend you look into using an open-source library that handles some of the difficulties. Here's two decent ones to check out:
https://github.com/AFNetworking/AFNetworking
https://github.com/pokeb/asi-http-request
Related
I've been reading about KVC and Cocoa Scripting, and how properties can be used for this. I have a model class in mind, but the element/property data has to be obtained from the Internet. But the design of properties and KVC looks like it assumes fast & in-memory retrieval, while network calls can be slow and/or error-prone. How can these be reconciled?
For speed, do we just say "screw it" and post a waiting icon? (Of course, we should keep things multi-threaded so the UI doesn't stop while we wait.)
If your property is supposed to be always available, we could set it to nil if the resource call gets an error. But we would have no way to get the specifics. Worse would be a property that supports "missing values," then nil would represent that and we would have no spare state to use for errors.
Although Apple-events support error handling, I couldn't use it because between my potentially error-generating model calls and the Apple event, the KVC layer would drop the error to the floor (of oblivion). The Scripting Bridge API saw this problem, since its designers added a secret protocol to handle errors.
Am I wrong? Is there a way to handle errors with KVC-based designs?
Addendum
I forgot to mention exceptions. Objective-C now supports them, but the little I read about them implies that they're meant for catastrophic "instead of straight crashing" use, not for regular error handling like in C++. Except for that, they could've been useful here....
I think I understand what you're asking now. I would say using KVC (or property getters) is not a good way to accomplish what you're trying to do. If the code gets called on the main thread, you will then block that thread. which you don't want to do. As you have discovered you'll also have a hard time returning other state information such as errors.
Instead, you should use block syntax to create an asynchronous method that operates on a background queue. Here is a basic template for what this might look like:
// called from main thread
- (void) fetchDataInBackgroundWithCompletionHandler:(void (^)(id responseData, NSError *error))handler
{
// perform in background
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^()
{
// perform your background operation here
// call completion block on main thread
dispatch_async(dispatch_get_main_queue(), ^{
if(// whatever your error case is)
{
handler(nil, error);
}
else // success
{
handler(responseData, nil);
}
});
});
}
This also gives you the benefit of being able to pass in as many other parameters are you want as well as return as many values as you want in the completion block.
Some very good examples of this pattern can be seen in AFNetworking, which is one of the more popular networking libraries written for iOS. All of the calls in the library can be made from the main queue and will return on the main queue asycnhronously while performing all networking in the background.
I've been developing an iPhone app, which handed by an experienced developer. I'm just an apprentice programmer and still struggling with practical Objective-C/iOS application development (I have learned Java and PHP on my own, but objective-c is nothing like these to me).
Our app is just another "web-centric" (I don't even know this word is appropriate...) app which heavily relies on server-side operations, making frequent http post request every single time (such as tracking user locations, send messages to another users etc.).
When I was assigned to develop this app, I saw in the code, that every single http request was written inside each method. Each request was done by dispatching another thread, and each action were written for those requests' response accordingly.
E.g.
-(void) methodA {
// Making http request headers...
// Dispatch another thread
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT , 0);
dispatch_async(queue, ^{
// Send synchronous request and handle the response...
});
}
-(void) methodB {
// Making http request headers...
// Dispatch another thread
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT , 0);
dispatch_async(queue, ^{
// Send synchronous request and handle the response...
});
}
The codes like above are every where when the app needs to send request to the server.
I'm wondering, why he didn't create a class that handles http requests.
In Java, you could create a class that make synchronous request to the server:
public class ClassHttpRequest {
public int makePost {
// Send synchronous request and return result...
}
}
Then make an instance of this class and execute it's instance method (in this case, makePost) inside a thread:
public class methodA {
Thread t = new Thread(new Runnable() {
public void run() {
public ClassHttpRequest requestHandler = new ClassHttpRequest();
if (success == requestHandler.makePost()) {
// Handle response...
}
}
}
});
t.start();
}
Is there any performance penalty or issues in creating a class and let it handles frequent http request in Objective-C? Or, it's just simply not "recommended" or something? I have heard that, in Objective-C, it is not common to use try-catch statement for exception handling, because it would consume much resources. I do have read several iOS and Objective-C books (and googled), but such kind of "practical" answer for real application development is hard to find, most of the time it's rather confusing to beginners like me.
I should ask him why he didn't create a such class, but he's away now and I couldn't get in touch with him. Also, I belive that the professionals here in stackoverflow can provide me much more accurate and concise solutions than my predecessor. (I have asked several questions and already got what I wanted to know.)
Thanks in advance.
Normal rules of object-oriented design apply: if it makes sense to represent a HTTP request as a tangible object - in particular, there's a bunch of boilerplate code that's necessary and would otherwise be copy-pasted - then it's probably a good idea to use a class. Otherwise, there's no need. Though in this specific case, is there a reason you're not just using the standard, asynchronous system APIs - NSURLRequest, NSURLConnection, NSURLDownload, etc?
#try/#catch are by definition used for exception handling, and should be used as necessary. If you skimp on them your code may fail in unnecessarily interesting ways (e.g. leaving locks dangling) or to unnecessarily degrees (e.g. crashing completely instead of simply failing a specific operation). What you shouldn't do is use them for flow control - unlike other languages, Objective-C exceptions are for programmer errors, "impossible" conditions, and other such events. Unfortunately a lot of existing Objective-C code is not exception-safe, so while you should utilise them you shouldn't rely on them.
They're not particularly expensive in any of the runtimes you're likely to use these days - the #try is very cheap, almost free. Only if an exception is thrown is there any significant work done, and since you should only be seeing them in very bad situations - i.e. not frequently - the performance cost is irrelevant.
Refactoring the code is a question of balance. The current code is verbose and a bit repeating, but refactoring it into a separate class will introduce a new indirection, an intermediate API. It’s probably worth it if the new API has a decent semantics, like if you can create a SomeNetworkService class with methods like postStatus, listItems and such. The interface should be asynchronous, something like this:
typedef void (^StatusCompletionBlock)(BOOL success, NSError *error);
- (void) postStatus: (NSString*) status withCompletion: (StatusCompletionBlock) completion;
This should make the code more readable, more DRY and even more testable, since you can replace the whole SomeNetworkService object with a stub. So that would be certainly worth it.
The performance hit of sending one extra message is not worth mentioning. Generally speaking, people worry about performance too much. If you can sacrifice performance for better readability, 99 times out of 100 it’s worth it.
I have been working with a few applications that deal with NSURLConnections. While researching best practices I have noticed a lot of examples online showing how to use NSOperation and NSOperationQueue to deal with this.
I have also noticed on stackoverflow a few examples that show initializing the connection as synchronous and asynchronous using the class methods of NSURLConnection: sendAsynchronousRequest and sendSynchronousRequest.
Currently I am doing my initialization as follows:
[[NSURLConnection alloc] initWithRequest:request delegate:self];
While doing this I have monitored the main thread and the calls to the delegate methods:
connectionDidFinishLoading, connectionDidReceiveResponse, connectionDidReceiveData and connectionDidFailWithError
Everything I have read in Apples documentation and my tests prove to me that this is asynchronous by default behavior.
I would like to know from more experienced Objective C programmers when the other options would be used for either a best practice, or just be more correct than what I see as the most simplistic way to get async behavior?
This is my first question I have posted on here, if more information is needed please ask.
Synchronous is bad bad bad. Try to avoid it. That will block up your main thread if the data transfer is large, thus resulting in an unresponsive UI.
Yes, it is possible to dispatch a synchronous call onto a different thread, but then you have to access any UI elements back on the main thread and it is a mess.
Normally I just use the delegate methods you have described - it is straightforward, and NSURLConnection already handles the asynchronous call for you away from the main thread. All you need to do is implement the simple delegate methods! It's a little more code, but you always want to go asynchronous. Always. And when it is finished loading, use the information you get to update the UI from the finishedLoading delegate method.
You also have the option of using blocks now, but I can't speak for how well those work or even how to use them well. I'm sure there's a tutorial somewhere - the delegate methods are just so easy to implement.
The method you list are the traditional means of asynchronous transfer and an app that uses them will be efficient in processor (and hence power) use.
The sendAsynchronousRequest method is a relatively new addition, arriving in iOS 5. In terms of best practice there's little other than style to differentiate between it and the data delegate methods other than that a request created with the latter can be cancelled and a request created with the former can't. However the tidiness and hence the readability and greater improbability of bugs of the block-based sendAsynchronousRequest arguably give it an edge if you know you're not going to want to cancel your connections.
As a matter of best practice, sendSynchronousRequest should always be avoided. If you use it on the main thread then you'll block the user interface. If you use it on any other thread or queue that you've created for a more general purpose then you'll block that. If you create a special queue or thread for it, or post it to an NSOperationQueue then you'll get no real advantages over a normal asynchronous post and your app will be less power efficient per Apple's standard WWDC comments.
References to sendSynchronousRequest are probably remnants of pre-iOS 5 patterns. Anywhere you see a sendSynchronousRequest, a sendAsynchronousRequest could be implemented just as easily and so as to perform more efficiently. I'd guess it was included originally because sometimes you're adapting code that needs to flow in a straight line and because there were no blocks and hence no 'essentially a straight line' way to implement an asynchronous call. I really can't think of any good reason to use it now.
I'm using NSOperationQueue, and NSOperation for running some function on background click.
But I want to be able, when user clicks some button, stop that Operation.
How can I do it?
Something like, [currentoperation stop];
Cancel - won't work me. I want to stop immediately.
Thanks
You should be calling the -cancel method, and the operation itself has to support being cancelled by monitoring the isCancelled property/keypath and safely stopping when its value becomes YES. If the NSOperation is your own, you will probably have to create a custom subclass to implement this functionality. You cannot (safely) force an arbitrary operation to immediately stop. It has to support being cancelled.
You can't stop immediately by using anything Apple provides with NSOperation. You can use -[cancel] as other people have suggested here, but the current operation will still run until completion. One way of getting close to use -[isCancelled] inside of your operation and sprinkle that throughout the code (especially in long running loops). Something like:
- (void)main {
// do a little work
if ([self isCancelled]) { return; }
// do a little more work
if ([self isCancelled]) { return; }
}
This way you'll get things stopped relatively soon.
If you're looking to really force the thread to stop, you may need to look into signal handling. There's a threaded example here. Sending a custom signal to a specific thread, you may be able to then terminate that thread in some way. This will be a lot more work, though, and is probably much more trouble than it's worth.
you use cancel, and test whether self (the NSOperation) has been cancelled during execution.
I have a NSOperationQueue with a number of NSOperations in it. I want to ensure that a particular part of the code is not executed in parallel. I use a NSLock object like this:
[myLock lock]
some critical code
[myLock unlock]
Unfortunately instead of a blocking "lock" call I get the following error:
-[NSLock lock]: deadlock ( '(null)')
After some investigation I noticed that all NSOperations seem to run in the same thread. I drew that conclusion after logging the thread id with:
NSLog(#"Thread %#\n", self, [NSThread currentThread]);
All operations seem to run in the same thread. Even though they are running in parallel as operations.
Does that make sense? I am a little confused. Do I miss something? Do you see any problem with using NSOperation and NSLock together? (If not, then I am sure the error is in my code)
I solved it. I am using ASIHTTPRequest underneath. Apparently all HTTP calls are made in the same thread unless you override "+ (NSThread *)threadForRequest:(ASIHTTPRequest *)request".
Sorry.