Question

I am trying to create a category for NSURLSession (see code below). The second I try to call the setCurrentRegion the app crashes. What am I doing wrong? Or can NSURLSession not be turned into a category because it uses a shared instance? If that is the case how could I pass data into the NSURLSession so I can retry processing a CLRegion if the network connection fails?

header file:

#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>

@interface NSURLSession (LocationSession)

@property (nonatomic, strong) CLRegion *currentRegion;

- (void)setCurrentRegion:(CLRegion *)region;
- (CLRegion *)currentRegion;

@end

implementation file:

#import "NSURLSession+LocationSession.h"

@implementation NSURLSession (LocationSession)

- (void)setCurrentRegion:(CLRegion *)region
{
    self.currentRegion = region;
}

- (CLRegion *)currentRegion
{
    return self.currentRegion;
}

@end

Code calling the category

    if (([self checkWifi] || [self checkWWLAN]) && [rUrl initializeRemote] && [rUrl initializeRemotePassword])
    {
        NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
        sessionConfig.allowsCellularAccess = YES;
        sessionConfig.timeoutIntervalForRequest = 10.0;
        NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];
        [session setCurrentRegion:region];
        [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
        [[session dataTaskWithURL:[NSURL URLWithString:[rUrl setDeviceTarget:[[gObject getSwitchID] stringValue] setServiceID:serviceName setAction:actionName setActionName:actionValue setActionValue:performAction]]
                 completionHandler:^(NSData *data,
                                    NSURLResponse *response,
                                    NSError *error)
        {
            backgroundTaskCount--;
            NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            id content = [responseString JSONValue];
            if ([data length] > 0 && error == nil)
            {
                NSDictionary *statusDict = [content valueForKey:responseName];
                NSString *status = [statusDict valueForKey:@"OK"];

                if ((NSNull *)status == [NSNull null] || status == nil)
                {
                    status = [statusDict valueForKey:@"JobID"];
                    showNotifications = [[NSUserDefaults standardUserDefaults] boolForKey:@"notifyGeo"];
                    if (showNotifications)
                    {
                        UILocalNotification *localNotification = [[UILocalNotification alloc] init];
                        localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:1];;
                        NSString *message = [[NSString alloc] init];
                        message = [@"Switch: " stringByAppendingString:status];
                        localNotification.alertBody = message;
                        localNotification.soundName = UILocalNotificationDefaultSoundName;
                        localNotification.timeZone = [NSTimeZone defaultTimeZone];
                        [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
                    }

                    NSLog(@"%@", @"Job ID Value");
                    NSLog(@"%d", [status intValue]);
                    NSLog(@"%@", @"Connection Successful");
                    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
                    if (backgroundTaskCount == 0)
                    {
                        [self endBackgroundTask];
                    }
                }else
                {
                    showNotifications = [[NSUserDefaults standardUserDefaults] boolForKey:@"notifyGeo"];
                    if (showNotifications)
                    {
                        UILocalNotification *localNotification = [[UILocalNotification alloc] init];
                        localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:1];;
                        NSString *message = [[NSString alloc] init];
                        message = [@"Switch: " stringByAppendingString:status];
                        localNotification.alertBody = message;
                        localNotification.soundName = UILocalNotificationDefaultSoundName;
                        localNotification.timeZone = [NSTimeZone defaultTimeZone];
                        [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
                    }

                    NSLog(@"%@", @"Job ID Value");
                    NSLog(@"%@", status);
                    NSLog(@"%@", @"Connection Successful");
                    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
                    if (backgroundTaskCount == 0)
                    {
                        [self endBackgroundTask];
                    }
                }
            }else
                if ([data length] == 0)
                {
                    NSLog(@"%@", [error debugDescription]);
                    NSLog(@"%@", [[session currentRegion] identifier]);
                    NSLog(@"%@", [[session currentRegion] debugDescription]);
                    NSLog(@"%@", @"Retrying Connection");
                    [locationManager startMonitoringForRegion:[session currentRegion]];
                    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
                }else
                    if (error != nil)
                    {
                        showNotifications = [[NSUserDefaults standardUserDefaults] boolForKey:@"notifyGeo"];
                        if (showNotifications)
                        {
                            UILocalNotification* localNotification = [[UILocalNotification alloc] init];
                            localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:1];
                            NSString *message = [[NSString alloc] init];
                            NSString *status = @"Connection Failure";
                            message = [@"Switch: " stringByAppendingString:status];
                            localNotification.alertBody = message;
                            localNotification.soundName = UILocalNotificationDefaultSoundName;
                            localNotification.timeZone = [NSTimeZone defaultTimeZone];
                            [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
                        }
                        NSLog(@"%@", [error debugDescription]);
                        NSLog(@"%@", [[session currentRegion] identifier]);
                        NSLog(@"%@", [[session currentRegion] debugDescription]);
                        NSLog(@"%@", @"Retrying Connection");
                        [locationManager startMonitoringForRegion:[session currentRegion]];
                        [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
                    }

        }] resume];
    }
}

Crash log:

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Triggered by Thread:  4

Last Exception Backtrace:
0   CoreFoundation                  0x182e86f50 __exceptionPreprocess + 132
1   libobjc.A.dylib                 0x18f3901fc objc_exception_throw + 60
2   CoreFoundation                  0x182e8bc04 -[NSObject(NSObject)    doesNotRecognizeSelector:] + 220
3   CoreFoundation                  0x182e89930 ___forwarding___ + 912
4   CoreFoundation                  0x182da95dc _CF_forwarding_prep_0 + 92
5   MyAPP                           0x1001c11b4 0x100084000 + 1298868
6   MyAPP                           0x1001be7c8 0x100084000 + 1288136
7   CoreLocation                    0x1833e31d4 ___lldb_unnamed_function261$$CoreLocation + 712
8   CoreLocation                    0x1833dedcc ___lldb_unnamed_function156$$CoreLocation + 76
9   CoreLocation                    0x1833d8f3c ___lldb_unnamed_function16$$CoreLocation +     96
10  CoreFoundation                  0x182e47680 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 20
11  CoreFoundation                  0x182e46838 __CFRunLoopDoBlocks + 300
12  CoreFoundation                  0x182e44c28 __CFRunLoopRun + 616
13  CoreFoundation                  0x182d85c20 CFRunLoopRunSpecific + 452
14  GraphicsServices                0x188a6dc0c GSEventRunModal + 168
15  UIKit                           0x185eb6fdc UIApplicationMain + 1156
16  MyAPP                           0x1001f9290 0x100084000 + 1528464
17  libdyld.dylib                   0x18f983aa0 start + 4


Thread 0:
0   libsystem_kernel.dylib          0x000000018fa7e868 __semwait_signal_nocancel + 8
1   libsystem_c.dylib               0x000000018f9efe5c usleep$NOCANCEL + 64
2   libsystem_c.dylib               0x000000018fa1280c abort + 116
3   MyAPP                           0x000000010021dedc 0x100084000 + 1679068
4   CoreFoundation                  0x0000000182e872e4 __handleUncaughtException + 640
5   libobjc.A.dylib                 0x000000018f3904c4 _objc_terminate() + 112
6   libc++abi.dylib                 0x000000018ec53164 std::__terminate(void (*)()) + 12
7   libc++abi.dylib                 0x000000018ec52d38 __cxa_rethrow + 140
8   libobjc.A.dylib                 0x000000018f3903a4 objc_exception_rethrow + 40
9   CoreFoundation                  0x0000000182d85c98 CFRunLoopRunSpecific + 572
10  GraphicsServices                0x0000000188a6dc08 GSEventRunModal + 164
11  UIKit                           0x0000000185eb6fd8 UIApplicationMain + 1152
12  MyAPP                           0x00000001001f928c 0x100084000 + 1528460
13  libdyld.dylib                   0x000000018f983a9c start + 0

Thread 1:
0   libsystem_kernel.dylib          0x000000018fa65aa8 kevent64 + 8
1   libdispatch.dylib               0x000000018f969998 _dispatch_mgr_thread + 48

Thread 2:
0   libsystem_kernel.dylib          0x000000018fa65cf4 semaphore_timedwait_trap + 8
1   CoreLocation                    0x00000001833d8e44 ___lldb_unnamed_function15$$CoreLocation + 356
2   CoreLocation                    0x00000001833dcbd4 ___lldb_unnamed_function66$$CoreLocation + 284
3   CoreLocation                    0x000000018340c974 ___lldb_unnamed_function1194$$CoreLocation + 340
4   libxpc.dylib                    0x000000018fb1b2a8 _xpc_connection_call_event_handler + 64
5   libxpc.dylib                    0x000000018fb1902c _xpc_connection_mach_event + 2128
6   libdispatch.dylib               0x000000018f9680a4 _dispatch_client_callout4 + 12
7   libdispatch.dylib               0x000000018f96a854 _dispatch_mach_msg_invoke + 148
8   libdispatch.dylib               0x000000018f96e450 _dispatch_queue_drain + 552
9   libdispatch.dylib               0x000000018f96a250 _dispatch_mach_invoke + 104
10  libdispatch.dylib               0x000000018f96e450 _dispatch_queue_drain + 552
11  libdispatch.dylib               0x000000018f96a4bc _dispatch_queue_invoke + 64
12  libdispatch.dylib               0x000000018f96e450 _dispatch_queue_drain + 552
13  libdispatch.dylib               0x000000018f96a4bc _dispatch_queue_invoke + 64
14  libdispatch.dylib               0x000000018f96f0f0 _dispatch_root_queue_drain + 100
15  libdispatch.dylib               0x000000018f96f4f8 _dispatch_worker_thread2 + 72
16  libsystem_pthread.dylib         0x000000018fafd6b8 _pthread_wqthread + 352
17  libsystem_pthread.dylib         0x000000018fafd548 start_wqthread + 0

Thread 3:
0   CoreFoundation                  0x0000000182d9b274 CFStringFindWithOptionsAndLocale + 4880
1   Foundation                      0x0000000183927798 -[NSString rangeOfString:options:range:locale:] + 488
2   MyAPP                           0x00000001002c5fb8 0x100084000 + 2367416
3   MyAPP                           0x00000001002c8414 0x100084000 + 2376724
4   MyAPP                           0x00000001001054f8 0x100084000 + 529656
5   libdispatch.dylib               0x000000018f968010 _dispatch_call_block_and_release + 20
6   libdispatch.dylib               0x000000018f967fd0 _dispatch_client_callout + 12
7   libdispatch.dylib               0x000000018f96e4a4 _dispatch_queue_drain + 636
8   libdispatch.dylib               0x000000018f96a4bc _dispatch_queue_invoke + 64
9   libdispatch.dylib               0x000000018f96f0f0 _dispatch_root_queue_drain + 100
10  libdispatch.dylib               0x000000018f96f4f8 _dispatch_worker_thread2 + 72
11  libsystem_pthread.dylib         0x000000018fafd6b8 _pthread_wqthread + 352
12  libsystem_pthread.dylib         0x000000018fafd548 start_wqthread + 0

Thread 4 Crashed:
0   libsystem_kernel.dylib          0x000000018fa7ee74 __workq_kernreturn + 8
1   libsystem_pthread.dylib         0x000000018fafd548 start_wqthread + 0

Thread 5 name:  com.apple.NSURLConnectionLoader
Thread 5:
0   libsystem_kernel.dylib          0x000000018fa65ca0 mach_msg_trap + 8
1   CoreFoundation                  0x0000000182e46b70 __CFRunLoopServiceMachPort + 180
2   CoreFoundation                  0x0000000182e44d00 __CFRunLoopRun + 832
3   CoreFoundation                  0x0000000182d85c1c CFRunLoopRunSpecific + 448
4   Foundation                      0x000000018397a424 +[NSURLConnection(Loader) _resourceLoadLoop:] + 344
5   Foundation                      0x0000000183a08408 __NSThread__main__ + 996
6   libsystem_pthread.dylib         0x000000018faffe18 _pthread_body + 164 
7   libsystem_pthread.dylib         0x000000018faffd70 _pthread_start + 136
8   libsystem_pthread.dylib         0x000000018fafd550 thread_start + 0

Thread 6:
0   libsystem_kernel.dylib          0x000000018fa7ee74 __workq_kernreturn + 8
1   libsystem_pthread.dylib         0x000000018fafd548 start_wqthread + 0

Thread 7:
0   libsystem_kernel.dylib          0x000000018fa7ee74 __workq_kernreturn + 8
1   libsystem_pthread.dylib         0x000000018fafd548 start_wqthread + 0

Thread 8 name:  com.apple.CFSocket.private
Thread 8:
0   libsystem_kernel.dylib          0x000000018fa7e76c __select + 8
1   libsystem_pthread.dylib         0x000000018faffe18 _pthread_body + 164
2   libsystem_pthread.dylib         0x000000018faffd70 _pthread_start + 136
3   libsystem_pthread.dylib         0x000000018fafd550 thread_start + 0

Updated code Header file

#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#import <objc/runtime.h>

@interface NSURLSession (LocationSession)

@property (nonatomic, retain) CLRegion *currentRegion;

@end

Implementation File

#import "NSURLSession+LocationSession.h"

static CLRegion *currentRegions;

@implementation NSURLSession (LocationSession)

- (void)setCurrentRegion:(CLRegion *)region
{
    objc_setAssociatedObject(self, &currentRegions, region, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (CLRegion *)currentRegion
{
    return objc_getAssociatedObject(self, &currentRegions);
}

@end
Was it helpful?

Solution

The compiler translates

self.currentRegion = region;

into

[self setCurrentRegion:region];

which means that you have an infinite recursion in your setter method

- (void)setCurrentRegion:(CLRegion *)region
{
    self.currentRegion = region;
}

And the same problem occurs in your getter method:

- (CLRegion *)currentRegion
{
    return self.currentRegion;
}

The program then crashes because of a stack overflow.

But the underlying problem is that you cannot add an instance variable to a class in a category method. The only solution for this problem that I know if is to use so-called "associated objects" to hold the contents of the property. See Objective-C Category and new iVar for an example.


Update: There are problems that are special to the NSURLSession class. This seems to be a class cluster, but quite unusually the concrete object returned by the factory method is not a subclass of NSURLSession:

NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];
BOOL b = [session isKindOfClass:[NSURLSession class]];
// --> NO !!!

This is the reason for the exception message

 -[__NSCFURLSession setCurrentRegion:]: unrecognized selector sent to instance ...

One workaround would be to add the property in a category to NSObject.

But as it turned out in the discussion, the property was meant to hold a reference to some object (the region) so that it would be available in the completion block of the dataTaskWithURL:completionHandler: method.

But that is not necessary (if I understand the problem correctly), because creating a block captures the values of variables that are referenced inside the block:

CLRegion *region = ...;
[[session dataTaskWithURL:[NSURL URLWithString:[rUrl setDeviceTarget:[[gObject getSwitchID] stringValue] setServiceID:serviceName setAction:actionName setActionName:actionValue setActionValue:performAction]]
                 completionHandler:^(NSData *data,
                                    NSURLResponse *response,
                                    NSError *error)
        {
             // ...
             NSLog(@"%@", [region identifier]);
             // ...
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top