LShi
LShi

Reputation: 1502

How can this AnyObject? has boolValue property

I have written an NSURL extension in Swift.

extension NSURL {
  var isDir : Bool {
    do {
      var isDir : AnyObject?
      try self.getResourceValue(&isDir, forKey: NSURLIsDirectoryKey)
      if let result = isDir?.boolValue {
        return result
      } else {
        return false
      }
    } catch {
      return false
    }
  }
}

That works. But I find some people "cast" the pointer (isDIR in this case) to NSNumber first. Examples are:

I've found no documentation says AnyObject conforms to the BooleanType protocol. So how does my code in Swift work?

Upvotes: 1

Views: 426

Answers (3)

Max
Max

Reputation: 22385

Adding on to vacawama's answer, the Swift documentation is worded in a slightly confusing way. It says that for AnyObject

@objc symbols are available as implicitly unwrapped optional methods and properties, respectively.

You might think this means everything is implicitly unwrapped, but that's not true. The methods are available as implicitly unwrapped optionals, but the properties types are all available as optionals. So while it is in generally unsafe to call a method on an AnyObject, it is safe to access a property.

For example,

let str = "Hello!" as NSString
let any = str as AnyObject
str.firstObject // compiler error, firstObject is an NSArray property
any.firstObject // nil

This is why you can safely call boolValue. It gets imported from NSNumber and its type is changed to Bool?.

Upvotes: 0

Pinecone
Pinecone

Reputation: 421

The documentation for getResourceValue(_ *value*:forKey:) indicates that _ *value*: is an AutoreleasingUnsafeMutablePointer<AnyObject?> type, which means it is fundamentally a void pointer which can be cast to any type so long as it is a proper type. Therefore, the direct cast of isDir to a Bool will work because that is basically what it is, a Boolean type:

let boolResult: Bool = isDir as! Bool

simply works as a direct cast because it is a void type to begin with.

Upvotes: 1

vacawama
vacawama

Reputation: 154731

Command-click on AnyObject and you will find the answer:

/// The protocol to which all classes implicitly conform.
///
/// When used as a concrete type, all known `@objc` methods and
/// properties are available, as implicitly-unwrapped-optional methods
/// and properties respectively, on each instance of `AnyObject`.  For
/// example:
///
///     class C {
///       @objc func getCValue() -> Int { return 42 }
///     }
///
///     // If x has a method @objc getValue()->Int, call it and
///     // return the result.  Otherwise, return nil.
///     func getCValue1(x: AnyObject) -> Int? {
///       if let f: ()->Int = x.getCValue { // <===
///         return f()
///       }
///       return nil
///     }
///
///     // A more idiomatic implementation using "optional chaining"
///     func getCValue2(x: AnyObject) -> Int? {
///       return x.getCValue?() // <===
///     }
///
///     // An implementation that assumes the required method is present
///     func getCValue3(x: AnyObject) -> Int { // <===
///       return x.getCValue() // x.getCValue is implicitly unwrapped. // <===
///     }
///
/// - SeeAlso: `AnyClass`
@objc public protocol AnyObject {
}

So, you can call any @objc method on an AnyObject instance.

If you type

let a: AnyObject?

in a Playground and then:

a?.

autocomplete will show you the complete list of methods you can call. And it is huge.

Upvotes: 2

Related Questions