iOS: NSURLSession category methods not being found at runtime

感情迁移 提交于 2019-12-18 04:36:24

问题


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 NSURLSessionUploadTasks, which get deserialized as __NSCFURLSessionUploadTasks 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!