3

I have the following code. It compiles, but crashes. What am I doing wrong ?

- (void)start: (int*)shouldPause
{        
    NSNumber * oShouldPause = (NSNumber *)shouldPause;
    [self performSelectorInBackground:@selector(runThread:) withObject: oShouldPause];
}

and function runThread is

- (void)runThread: (NSNumber*) shouldPause

In general, I want to set some integer outside of the thread and inside of the thread to change behaviour depending on this integer.

Thanks for help!

Bill Lumbert
  • 4,633
  • 3
  • 20
  • 30

2 Answers2

8
NSNumber * oShouldPause = @(*shouldPause);

What you did is a common mistake. NSNumber is an object while int is a primitive type. You cast a pointer to a primitive type to a pointer to an object. Compiler allows that as they both are just pointers. Further the code expects an object, trying to send a message and boom. Something went wrong.

Most likely you don't need int* as a parameter. Just int. And therefore then you need to create the number object as follows, without dereferencing:

NSNumber * oShouldPause = @(shouldPause);
Andrey Chernukha
  • 21,488
  • 17
  • 97
  • 161
  • Your first edit usurped the answer I was typing and therefore acquired my vote; there's a huge difference between primitive and object types, and with tagged pointers an `NSNumber` doesn't even necessarily exist anywhere in memory. Objects are reflective and have a hierarchy, primitive types are not and do not. An `int` looks nothing like an `NSNumber` at the machine level, which is what's required for a pointer cast to make sense. – Tommy Aug 08 '18 at 16:48
2

As Andrey Chemukha already cited, you cannot cast an int * to NSNumber *.

Another viable option, if you really have a need to pass the pointer versus the contents of the pointer (which is what Andrey has provided code for) is use NSValue. An example of why you may need this is if you have a deferred call but you want that call to have the most up to date value. Note I'm ignoring synchronization aspects here for simplicity.

And while you're at it you should clean up your usage of data types and use either a bool or BOOL to hold your flag.

- (void)start:(BOOL *)shouldPause {
    NSValue *oShouldPause = [NSValue valueWithPointer:shouldPause];
    [self performSelectorInBackground:@selector(runThread:) withObject:oShouldPause];
}

- (void)runThread:(NSValue *)value {
    BOOL *shouldPause = (BOOL *) [value pointerValue];
    if (shouldPause) {
        // Pause-a-licous
        NSLog(@"shouldPause is %d", *shouldPause);
    }
}

NOTE THIS HAS CAVEATS!! Meaning, you need to guarantee the lifetime of the pointer else you will have a dangling pointer. So doing this is not for the meek.

To be clear, based on what you asked, I think Andrey's answer is the right thing to do. I'm just adding this as another option for your toolbox.

Also given how you are doing things, you don't even need to use performSelector:withObject:.

- (void)startAlternate:(BOOL *)shouldPause {
    [self runThreadAlternate:shouldPause ? *shouldPause : NO];
}

- (void)runThreadAlternate:(BOOL)shouldPause {
    // Pause-o-matic
    NSLog(@"shouldPause is %d", shouldPause);
}

I'd probably even then go as far as to change the argument to startAlternate: be a BOOL and not a pointer to BOOL. More often than not, it's best to keep things simple.

Mobile Ben
  • 7,121
  • 1
  • 27
  • 43