问题
I wanted to create a category on NSURLSession. The app compiles ok, but when I attempt to call the category methods I get
-[__NSCFURLSession doSomething]: unrecognized selector sent to instance 0xbf6b0f0
<unknown>:0: error: -[NSURLSession_UPnPTests testCategory] : -[__NSCFURLSession doSomething]: unrecognized selector sent to instance 0xbf6b0f0
Very strange, here is a test class I built to show the issue. The category on the NSString works fine, but the category on NSURLSession fails to find the method at runtime. I suspect this is something internal.
Opinions please :-)
#import <XCTest/XCTest.h>
@interface NSString (test)
-(void) doSomethingHere;
@end
@implementation NSString (test)
-(void) doSomethingHere {
NSLog(@"Hello string");
}
@end
@interface NSURLSession (test)
-(void) doSomething;
@end
@implementation NSURLSession (test)
-(void) doSomething {
NSLog(@"Hello!!!!");
}
@end
@interface NSURLSession_UPnPTests : XCTestCase
@end
@implementation NSURLSession_UPnPTests
-(void) testCategory {
[@"abc" doSomethingHere];
NSURLSession *session = [NSURLSession sharedSession];
[session doSomething];
}
@end
回答1:
I've had similar results with backgrounded NSURLSessionUploadTask
s, which get deserialized as __NSCFURLSessionUploadTask
s during the -URLSession:task:didCompleteWithError:
delegate callback.
If I were you, I'd give up on that approach, and use composition (i.e. make the NSURLSession
an ivar in another object). If you need to store some info with the NSURLSession
, you could stuff a JSON-encoded dictionary into the .sessionDescription
.
Here's the code that I used to do that for a task:
#pragma mark - Storing an NSDictionary in NSURLSessionTask.description
// This lets us attach some arbitrary information to a NSURLSessionTask by JSON-encoding
// an NSDictionary, and storing it in the .description field.
//
// Attempts at creating subclasses or categories on NSURLSessionTask have not worked out,
// because the –URLSession:task:didCompleteWithError: callback passes an
// __NSCFURLSessionUploadTask as the task argument. This is the best solution I could
// come up with to store arbitray info with a task.
- (void)storeDictionary:(NSDictionary *)dict inDescriptionOfTask:(NSURLSessionTask *)task
{
NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil];
NSString *stringRepresentation = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[task setTaskDescription:stringRepresentation];
[stringRepresentation release];
}
- (NSDictionary *)retrieveDictionaryFromDescriptionOfTask:(NSURLSessionTask *)task
{
NSString *desc = [task taskDescription];
if (![desc length]) {
DDLogError(@"No description for %@", task);
return nil;
}
NSData *data = [desc dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *dict = (data ? (id)[NSJSONSerialization JSONObjectWithData:data options:0 error:nil] : nil);
if (!dict) {
DDLogError(@"Could not parse dictionary from task %@, description\n%@", task, desc);
}
return dict;
}
回答2:
Here is an alternative solution if you really want to add category to NSURLSession. I found this solution from BETURLSession. Of course, you have to be really careful about the name to avoid name conflicting.
Code for header
#import <Foundation/Foundation.h>
@interface NSURLSession (YTelemetry)
-(void) hello;
@end
Code for implementation
#import "NSURLSession+YTelemetry.h"
@implementation NSObject (YTelemetry)
-(void) hello
{
NSLog(@"hello world");
}
@end
Now in the code, I can
NSURLSession *session = [NSURLSession sharedSession];
[session hello];
回答3:
I tried to do the same as @clay-bridges (i.e. storing a userInfo NSDictionary
for each task), and it works great when using a NSURLSession
with a session configuration different from background. If you use a NSURLSession
with a background configuration, and your app is terminated or crashes, the upload/download continues in the background, managed by the OS. When the upload/download finishes, if I try to retrieve the userInfo NSDictionary
previously stored, I get nothing. My solution is to store that custom object/NSDictionary
in the request, not the task, using NSURLProtocol
: +setProperty:forKey:inRequest:
, and later on, retrieve the information using:
id customObject = [NSURLProtocol propertyForKey:@"yourkey" inRequest:sessionTask.originalRequest];
回答4:
you must import the your custom category where you call the custom methods
#import "NSURLSession+test.h"
来源:https://stackoverflow.com/questions/21629682/ios-nsurlsession-category-methods-not-being-found-at-runtime