How is it possible to pass NSNumber to a method expecting a bool?
Asked Answered
K

3

6
[[self.view.window subviews] makeObjectsPerformSelector:@selector(setUserInteractionEnabled:) withObject:[NSNumber numberWithBool:NO]];

I saw this code in another question's answer ( How to disable touch input to all views except the top-most view?) and it surprised me when it worked, as setUserInteractionEnabled: expects a BOOL (which as it's not an objective-c object can't be passed in performSelector:withObject: type methods).

Where is the documentation that says that passing an NSNumber is ok? Does it work for all methods, or is a special implementation needed? And does it only work with BOOLs, or can it be done with types like int?

Karakalpak answered 1/1, 2012 at 21:53 Comment(4)
This search turns up lots of results of people trying similar things, but I don't see this mentioned as a solution, and I don't see how it could work.Garrotte
Are you saying you've tried this and it works, or you've seen someone saying it works?Whipperin
@jrturton, two people said it worked and it had the desired effect when I tried it, but I think I know what happened...Karakalpak
@Whipperin what happened was I trying to make all the subviews of a view have user interaction enabled, so I was using the code in the linked question, and it must be using the address of the NSNumber or something, and with a BOOL anything not 0 is true, as the NSNumber was not nil, the address would not be 0. However I'm in the comments of the answer others got it to work with false, and the answer is up votes twice and accepted?Karakalpak
W
3

If you just want your code to work, upvote and accept Paul.s's answer. If you want to read about nerds performing experiments, carry on. There was some interesting discussion in the comments to my original answer which I have summarised below.

This does not work. I have tried it in an iOS project, the userInteractionEnabled property is not affected by sending performSelector:withObject:. This matches up with the documentation for NSObject which states:

aSelector should identify a method that takes a single argument of type id. For methods with other argument types and return values, use NSInvocation.

The now legendary Peter Hosey built a sample foundation tool here which bizzarely enough works when you pass a double to it, also a float as I found out myself.

To add curiosity to curiosity, this does not work in an iOS project (double or float).

In summary, I think we can say the following:

  • If it ever works, it works by accident and should not be relied upon
  • An accepted or upvoted answer on stack overflow is not necessarily correct
Whipperin answered 1/1, 2012 at 22:30 Comment(8)
Is that documented anywhere? I know it's documented for KVC, but what about performSelector:withObject: and friends?Giese
I've no idea, I'm taking the questioners word for it that it works, I'm not able to check myself at the moment.Whipperin
This doesn't work for me on 10.7.2 with a BOOL (it's reporting -64 rather than 0), but surprisingly does work with a double.Giese
@PeterHosey, I tried with an int, after realising that BOOL actually didn't work? I actually can't get any to work.Karakalpak
@PeterHosey, that is bizarre, the double works in your code, but in the code I had it didn't? Although I was running a iOS project (static library) when i was testing it.Karakalpak
@PeterHosey that was pretty much the test code I was going to write, thanks for posting it. I think we've come to the conclusion that it doesn't (or shouldn't) work - do you think I should delete this answer or is it useful discussion to have around?Whipperin
@Whipperin it seems like useful information, although you should probably update your answer with the findings as most people are unlikely to trawl through the comments.Zechariah
@Pauls I will do, that is a good idea. I'll be sure to recommend that yours is the actual correct answer as well, the votes are misleading on this one.Whipperin
D
2

You cannot pass an NSNumber as the object and have it come out as a BOOL on the other end like that. However, there are some workarounds.

  1. Use an NSInvocation. You'd need to create an NSArray category that uses NSInvocation. This is a little messy.

  2. Create a UIView category with a setUserInteractionEnabled:-like function (with a similar name?) that operates with an NSNumber which then calls setUserInteractionEnabled: with the BOOL value of the NSNumber. Something like:

    @implementation UIView (Additions)
    - (BOOL)setUserInteractionEnabled2:(NSNumber *)aBool
    {
      self.userInteractionEnabled = [aBool boolValue];
    }
    @end
    
Dardanelles answered 1/1, 2012 at 22:33 Comment(0)
Z
2

I think you already came to the conclusion that this does not work.

@senojsitruc suggests a couple of different ways of doing it but overlooks the easiest solution

[self.view.window.subviews setValue:[NSNumber numberWithBool:NO] forKey:@"userInteractionEnabled"]; 

The docs for NSArray state:

setValue:forKey:

Invokes setValue:forKey: on each of the array's items using the specified value and key.

Zechariah answered 2/1, 2012 at 3:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.