I'm looking for a "did finish, or finish" thing in NSURL -> NSstring initWithContentsOfUrl
I found "URLResourceDidFinishLoading" but this seems to be depreciated
I couldn't find any but maybe I searched for the wrong thing.
If you're talking about -initWithContentsOfURL: from NSString, it has been deprecated.
But even if you are using it, it's a synchronous method - meaning your code will halt until the data has been loaded into the resulting NSString object:
NSURL *url = [NSURL URLWithString:#"http://google.com"];
NSString *htmlData = [NSString stringWithContentsOfURL:url];
NSLog(#"%#", htmlData); // you have your data loaded here, synchronously.
So, just to be clear: this is a bad practice for two reasons:
You're using a deprecated method which will most likely be removed on future SDK versions, and;
You're freezing your UI while loading something from the network, while giving no feedback to the user whatsoever.
What you need is probably NSURLRequest to create your request object and NSURLConnection to actually load it from the network.
Related
I have subclassed AFNetworking's AFHTTPSessionManager to create my own http client, I wrote a lot of categories, utilities methods, oauth2, retry login and whatnot.
I have this method to create the sharedSession:
+ (MTHTTPClient *)sharedSession {
static MTHTTPClient *sharedSession = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURL *baseURL = [NSURL URLWithString:[NSString stringWithFormat:#"%#://%#:%#", kABXMyAppProtocol, kABXMyAppHost, kABXMyAppPort]];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
...
});
return sharedSession;
}
and everything is fine.
Now I have this requirement to create another client but just with a different NSURLSessionConfiguration, for background download/uploads.
I would like to retain all the niceties and categories I built around this http client.
I'm unsure how to proceed, building another class (or singleton subclass, which kinda smells) seems to be not really viable due to the amount of logic I have to duplicate.
Creating another instance method like the one shown above would work? It's my understanding that the purpose of a singleton is to keep only one instance of the class around, so it may be a bad idea or not work at all.
Yes, it’s totally cool to create another singleton. There’s nothing magic about a singleton, it’s just an instance you create and re-use. You could have one or a thousand. As long as they’re stored in different static variables you’ll be fine.
we build an iPad app that downloads a bunch of data and PDF documents from a web service (data first, documents later in the background). To do so, we use SOAP via HTTP(S) requests. It works fine and altogether, the app is running well. Problem is, if there are too many documents to download at some point the app crashes. Using Instruments I figured out that it is a memory issue, particularly NSRegularExpression and NSRunLoop. (I'm using ARC)
I could improve my code to optimize the NSRegularExpression creation. But I don't know how to improve the NSRunLoop issue.
I tried both, asynchronous and synchronous HTTP request. Using async, I had to wait for the download to finish and since sleep()/[NSThread sleepForTimeInterval:] aren't an option, I use
while ( _waitToFinish ) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
Using sync request, Instruments reveals that
[NSURLConnection sendSynchronousRequest:theRequest returningResponse:&urlResponse error:&error];
also "waits" with help of NSRunLoop and also messes up the memory.
Is this a bug in CoreFoundation or ARC?
Is there another way to idle while waiting for the requests to finish?
Thanks in advance.
Edit:
With "memory issue" I meant that the app crashes (or is killed by iOS) because it uses too much memory.
This is what Instruments shows:
The percentage get higher the longer the app is downloading.
Edit:
Going further down revealed that it is NSURLConnection, that is messing up the memory. It seems that I have somehow missed setting connection and receivedData to nil (see URL Loading System Programming Guide). This improved my memory usage again a little.
Now, there are two more big memory allocation operations:
And this is the code I think belongs to what Instruments displays:
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[_receivedData appendData:data];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *responseText = [[NSString alloc] initWithBytes:[_receivedData mutableBytes] length:[_receivedData length] encoding:NSUTF8StringEncoding];
self.lastResponse = responseText;
responseText = nil;
connection = nil;
_receivedData = nil;
_lastResult = TRUE;
_waitToFinish = FALSE;
}
Is there anything I could change to improve the code?
Edit: (Changed title from "NSRunLoop messes up iPad memory")
Edit:
I created a test app to prove that it is the NSURLConnection, that messes up the memory. Then I contacted the Apple Developer Support.
Since I am downloading a lot of PDF in an iteration with NSURLConnection, the solution was to add an #autoreleasepool { .. } in the iteration and another one around the NSRunLoop.
Thanks.
It's not a bug in Core Foundation or ARC. It's a bug in your code.
Don't run the run loop yourself. Just use +[NSURLConnection sendAsynchronousRequest:queue:completionHandler:]. It will call your completionHandler block when the request is complete. That's when you process the response. Meanwhile, just return out of your method and let the system worry about running the run loop.
You say “it's a memory issue” with NSRegularExpression and NSRunLoop, but you don't say what the issue is, or what evidence Instruments is showing you. If you describe the “issue” in more detail, maybe we can help you with that.
In my app I am working with the SimpleKML framework
https://github.com/mapbox/Simple-KML
I have some big files which takes about 5 seconds to process thought this library. I was thinking to cache the object in something like NSData.
I have read this tutorial from Apple:
https://developer.apple.com/library/mac/#documentation/cocoa/conceptual/Archiving/Articles/archives.html
But i dont really know of using nscoder is the best way to do this. Can someone point me in the right direction?
You want to use NSKeyedArchiver and NSKeyedUnarchiver.
If the object you're saving (and the objects it has as properties, and their properties, etc.) implement the NSCoding protocol then it's, as simple as
NSData *savedData = [NSKeyedArchiver archivedDataWithRootObject:someObject];
[savedData writeToFile:pathToSaveFile atomically:YES];
to archive the object into an NSData and then save it to disk, and then later
NSData *loadedData = [NSData dataWithContentsOfFile:pathToSaveFile];
SomeClass *foo = [NSKeyedUnarchiver loadedData]
to load the data and unarchive the object from the data.
If not, you'll need to override initWithCoder: and encodeWithCoder: for the objects in question to make them serialize properly.
Alright, I have a rather odd question here. I feel much more comfortable writing code in Objective-C over any other language. I recently had to do some server-side programming, which required me to learn PHP. It works, yeah, but for fun I want to achieve the same thing through Objective-C. So, I created a binary using Xcode's Foundation preset. Here's most of the binary:
#import <Foundation/Foundation.h>
#import "JSONKit.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *theURL = [NSString stringWithFormat:#"http://blahblahblah.com/blah"];
NSError *err = nil;
NSURLResponse* response = nil;
NSMutableURLRequest* request = [[[NSMutableURLRequest alloc] init] autorelease];
NSURL*URL = [NSURL URLWithString:theURL];
[request setURL:URL];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[request setTimeoutInterval:30];
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
NSDictionary *someData = [data objectFromJSONData];
NSString *someString = [[someData objectForKey:#"foo"]objectForKey:#"bar"];
//do something
[pool drain];
return 0;
}
Quite basic code. It simply downloads some stuff from my server and I parse the JSON result and get a string that I want to use. So, my question is - how can I run this on my Linux-based server? I know it's possible, maybe using GNUStep (or cocotron?), which I don't know how to use. Anyone have ideas?
Well,
I suggest the same thing as the #lacqui.. Use CGI to run your program.. and here's the steps ..
(Side note: using CGI is deprecated as it starts a process each time a request is coming to the server (modern servers/web containers initiate a new thread (vs process).)
So, let's Start:
The input at hand is a program written in Objectiv-c
The output is a CGI script(program or whatever they name it) that
will run inside some http server.
First, let me ask you, What's the target platform to deploy your application?
If target deployment platform is a Mac then you will have to get the binary out of xcode ( I think it would be in a .dmg format) and find some where how to run a .dmg as a CGI program inside a web server ( I am not sure if apache runs under Mac or not)
But If it is Windows or Linux:
You will need to compile your application using GNUstep (I know nothing about portability from Xcode to GNUstep) You will need a GNUstep. Steps to install GNUstep either for Windows or Linux is trivial.
Once Installing GNUstep, you will have to compile your application again using it, refer to the same two links above to know how to compile your application.
The issue here is, AFAIK, GNUstep don't fully support Objc-2, so possibilities that the compilation will fail cause of usage of JSONKit.h is high. If your program compiles successfully, then you are almost done.
Suppose your program compiles, And you now have the binary program.. You will need to deploy it in some HTTP server that have CGI enabled. You can follow my blogpost here to know how to deploy a binary program written in C into some small http server called mini-httpd on Linux (it should apply to any binary program regardless of its source language).
What you want to look at is called the Common Gateway Interface. It is a protocol that states the way a web server will interact with subordinate processes.
What will happen is that, when a user browses to the URL that is mapped to your program, the server will start your program, and put the text of the request into STDIN. Your program will do whatever processing is required, then put the results (as well as some header information) into STDOUT.
What goes wrong when you try? You should be able to compile it with the GCC's Objective-C compiler. You should be able to run it.
Ever since switching to XCode 4 the leaks tool shows a LOT of leakage, all from JSONKit and ASIHTTPRequest, after a 2 min run I am leaking hundreds of arrays/dictionaries/strings (from jk_create_dictionary, jk_parse_array, HTTPMessage::*, etc.) totaling a few 100s KB. Most of the stack traces don't originate in any of my calls, and the rest are completely innocent.
I am pretty positive it was not the case pre-XCode 4.
I don't know who the culprit is. Any insight would be lovely.
Update:
The JSONKit leaks are probably JSONDecoder caching.
For example:
static JSONDecoder *decoder = nil;
if (!decoder)
decoder=[[JSONDecoder alloc] init];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:url]];
[request setCachePolicy:ASIDoNotWriteToCacheCachePolicy];
[request setCompletionBlock:^{
NSData *response = [request responseData];
NSDictionary *json = [decoder objectWithUTF8String:[response bytes] length:[response length]];
// ...
}];
[request setFailedBlock:^{
// ...
}];
[request startAsynchronous];
EDIT : Before you read the rest of this answer:
If you see that kind of memory leaks, don't blame Instruments or JSONKit... Both are reliable!
...Blame yourself, 99.9% chances your code is leaking the data you parsed with JSONKit!
END_OF_EDIT
Not an answer, more a complement, and an attempt to understand what's going on since I'm seeing leaks too with instruments.
I'm using JSONKit that way :
NSArray *lines = [dataString componentsSeparatedByString:#"\n"];
for (NSString *line in lines) { // I know, strange format isn't? :)
NSDictionary *json = [line objectFromJSONStringWithParseOptions:JKParseOptionLooseUnicode];
// use dictionary data...
}
#ssteinberg, is that the kind of leaks you're having? :
Note that I had this after some heavy load testing, 500 requests with huge JSON responses, which explains leaks are in MB (using latest gh version)
Please note that I'm quite new using Instruments, and I'm not sure how to understand these results. According to Frames reported, yes that looks like Caching... but I'd like to be sure...
So I opened an Issue on GH, I hope that #johnezang, or anyone, will enlight us about this.
All my apologies if that's just a misunderstanding with Instruments, which I would prefer :)
According to Apple's WWDC 2010 videos (Advanced Memory Analysis with Instruments), false positive leaks are rare. Sometimes the Leaks tool misses leaks, but it's reliable for the ones it reports. I'm not all that great with statics, but have you checked to make sure you're not alloc'ing the decoder without releasing it? If it's not released and falls out of scope, that would constitute a leak, right?