iOS: методы категории NSURLSession не найдены во время выполнения

Я хотел создать категорию в NSURLSession. Приложение компилируется нормально, но когда я пытаюсь вызвать методы категории, я получаю

-[__NSCFURLSession doSomething]: unrecognized selector sent to instance 0xbf6b0f0
<unknown>:0: error: -[NSURLSession_UPnPTests testCategory] : -[__NSCFURLSession doSomething]: unrecognized selector sent to instance 0xbf6b0f0

Очень странно, вот тестовый класс, который я создал, чтобы показать проблему. Категория в NSString работает нормально, но категория в NSURLSession не может найти метод во время выполнения. Подозреваю, что это что-то внутреннее.

Мнения пожалуйста :-)

#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

person drekka    schedule 07.02.2014    source источник


Ответы (4)


У меня были аналогичные результаты с фоновыми NSURLSessionUploadTasks, которые десериализуются как __NSCFURLSessionUploadTasks во время обратного вызова делегата -URLSession:task:didCompleteWithError:.

На вашем месте я бы отказался от этого подхода и использовал композицию (т.е. сделал NSURLSession иваром в другом объекте). Если вам нужно сохранить некоторую информацию с помощью NSURLSession, вы можете поместить словарь в формате JSON в файл .sessionDescription.

Вот код, который я использовал для этого для задачи:

#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;
}
person Clay Bridges    schedule 10.02.2014
comment
Ага. Я уже переписал, чтобы использовать композицию. Хотя мне все еще любопытно узнать первопричину. Об ошибке сообщили в Apple. - person drekka; 11.02.2014
comment
Вы когда-нибудь узнавали что-нибудь еще по этому вопросу? У меня такая же проблема. Есть ли ответ от Apple на ваш отчет об ошибке? Я хотел бы иметь возможность использовать категорию, если это вообще возможно. - person stevekohls; 24.06.2014
comment
@drekka: Не могли бы вы опубликовать ссылку на ошибку на форуме Apple? Даже я сталкиваюсь с аналогичной проблемой и не могу вызывать какие-либо пользовательские методы подкласса/категории NSURLSession из любого места, но только в iOS7. Он отлично работает в ›=iOS8. Пожалуйста, дайте мне знать, если вы можете помочь мне в любом случае. - person Rohit saraf; 15.12.2015

Вот альтернативное решение, если вы действительно хотите добавить категорию в NSURLSession. Я нашел это решение в BETURLSession. Конечно, вы должны быть очень осторожны с именем, чтобы избежать конфликта имен.

Код для заголовка

#import <Foundation/Foundation.h>

@interface NSURLSession (YTelemetry)

-(void) hello;
@end

Код для реализации

#import "NSURLSession+YTelemetry.h"

@implementation NSObject (YTelemetry)

-(void) hello
{
    NSLog(@"hello world");

}
@end

Теперь в коде я могу

NSURLSession *session = [NSURLSession sharedSession];

[session hello];
person jqyao    schedule 26.06.2014
comment
Проблема в том, что объекты NSURLSession преобразуются в их аналоги Core Foundation. Использование этого подхода по-прежнему не позволяет вам взаимодействовать с объектом self внутри метода категории. - person mph; 05.11.2014
comment
@jqyao он уже упомянул в своем коде, что мы не можем создать категорию в NSURLSession. Поэтому, пожалуйста, создайте категорию в NSObject. - person Saurav Nagpal; 23.12.2014
comment
Это немного хак, но он работает. Чтобы получить доступ к методам NSURLSession внутри метода категории, убедитесь, что объект является NSURLSession, а затем создайте локальную переменную, которая приводит себя к типу NSURLSession. - person SwampThingTom; 09.02.2015
comment
вы можете префикс своего метода, например. -(void)jq_hello - person malhal; 20.04.2015

Я попытался сделать то же самое, что и @clay-bridges (т.е. сохранить userInfo NSDictionary для каждой задачи), и это прекрасно работает при использовании NSURLSession с конфигурацией сеанса, отличной от фоновой. Если вы используете NSURLSession с фоновой конфигурацией и ваше приложение завершает работу или дает сбой, загрузка/загрузка продолжается в фоновом режиме под управлением ОС. Когда загрузка/загрузка завершается, если я пытаюсь получить ранее сохраненную информацию о пользователе NSDictionary, я ничего не получаю. Мое решение состоит в том, чтобы сохранить этот пользовательский объект/NSDictionary в запросе, а не в задаче, используя NSURLProtocol: +setProperty:​forKey:​inRequest:, а затем получить информацию, используя:

id customObject = [NSURLProtocol propertyForKey:@"yourkey" inRequest:sessionTask.originalRequest];

person Luis Valdés    schedule 17.03.2015

вы должны импортировать свою пользовательскую категорию, в которой вы вызываете пользовательские методы

#import "NSURLSession+test.h"
person alessio271288    schedule 07.02.2014