Re: NSRunningApplication executableURL issue in Swift
Re: NSRunningApplication executableURL issue in Swift
- Subject: Re: NSRunningApplication executableURL issue in Swift
- From: Bavarious <email@hidden>
- Date: Wed, 11 Mar 2015 02:26:27 -0300
> On Mar 10, 2015, at 21:17, Roland King <email@hidden> wrote:
>
>
>> On 11 Mar 2015, at 00:24, Bill Cheeseman <email@hidden> wrote:
>>
>> I'm using Xcode 6.2 and Swift 1.1.
>>
>> 1. The following statement reports the error "'NSURL?' does not have a member named 'path'" even though executableURL has a trailing exclamation point to unwrap it. The caret identifying the location of the error is placed under the exclamation point.
>>
>> for thisApp in NSWorkspace.sharedWorkspace().runningApplications {
>> let thisPath = thisApp.executableURL!.path // ERROR
>> }
>>
>> If I change executableURL to bundleURL, the error goes away. Yet executableURL and bundleURL are declared identically according to the NSRunningApplication reference document. As far as I know, bundleURL and executableURL both satisfy fileURL.
>>
>> The error also goes away if I assign thisApp to a local variable and expressly downcast it to NSRunningApplication, as shown below. Why would I need to do that with executableURL, when it isn't necessary with bundleURL?
>>
>> for thisApp in NSWorkspace.sharedWorkspace().runningApplications {
>> let myAppAndIMeanIt = thisApp as NSRunningApplication
>> let thisPath = myAppAndIMeanIt.executableURL!.path // NO ERROR
>> }
>>
>> Is this a Swift bug?
>>
>
> No, one of yours again - although I don't really understand what's going on with the plethora of optionals it's pretty easy, using something I've recommended dozens of times, to gain insight into what the compiler thinks it's doing by, as usual, splitting the line up into two assignments and using opt option+click to see what type it has guessed things are.
>
> In the first case if you split out thisApp.executableURL into its own line you will find it's of type 'NSURL?!'. I actually don't know entirely what that means, an implicitly unwrapped optional to an optional. I suppose it's means it's possibly something which is possibly an NSURL with an implicit guarantee that it's not nothing at all.
.executableURL has type NSURL? in all Cocoa classes that provide it (NSBundle & NSRunningApplication), so that’s one optionality level.
Note that `thisApp` has type AnyObject because NSWorkspace.runningApplications() returns [AnyObject]. But thisApp.executableURL forces Swift to downcast `thisApp` to some type that provides .executableURL, and this downcast might succeed or not — there’s the other optionality level.
> Anyway your '!' before '.path' just unwraps the implicitly unwrapped optional bit. Yes I know it's implicitly unwrapped already but I believe the '!' just unwraps it from implicitly unwrapped optional to optional NSURL to really unwrapped, ie optional NSURL. So you need two '!!', one for the '!' and one for the '?'.
>
> let thisPath = thisApp.executableURL!!.path
>
> I tried to move the ugliness around a bit, you can do
>
> for thisApp in NSWorkspace.sharedWorkspace().runningApplications as [ NSRunningAppliction ]
> {
> let thisPath = thisApp.executableURL!.path
> }
>
> except in 6.3 now you can't do that, you have to have 'as!', so it isn't much nicer although possibly more consistent
>
>
> Why does this happen with executableURL and not bundleURL, I don't really know. Both those properties are defined on both NSBundle and NSRunningApplication however the bundleURL is non-optional on both of them and the executableURL is optional on NSRunningApplication but not on NSBundle. Since runningApplications returns an [ AnyObject ] I assume there's one level of optionality in err ... something and then another one in executableURL because it might be optional, or not.
Much like in Objective-C. If you write
void someFunction(id obj) {
NSLog(@"%@", [obj bundleURL]);
}
the compiler picks *some* method that matches that selector—which method is undefined. This can be problematic when multiple methods have the same selector but different types; in particular, when one method has a floating-point type in its signature and another method doesn’t.
In Objective-C, -bundleURL isn’t a problem. In Swift, it can be a problem because NSBundle.bundleURL has type NSURL (not an optional) whereas NSRunningApplication.bundleURL has type NSURL? (an optional). In Bill’s case, the compiler is choosing NSBundle.bundleURL. Which method/property is chosen is probably undefined in Swift as well.
>
> Amusing crash of the day whilst trying to work this out
>
> import Cocoa
>
> for thisApp in NSWorkspace.sharedWorkspace().runningApplications
> {
> //let pp = thisApp as! NSRunningApplication
> let tt = thisApp.bundleURL!.path
> }
>
> works fine. However if you uncomment the commented line, which does nothing but set an unused variable, the line below crashes with EXC_i386_INVOP. That's 6.3, in 6.2 you have to remove the '!' in the commented line after the 'as', however you still get the same crash.
>
> All this to work around nil, a concept I've always rather liked.
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden