20

In Objective-C, this was as simple as:

[NSString stringWithFormat:@"%p", objRef]

How can I do this in Swift?

aleclarson
  • 18,087
  • 14
  • 64
  • 91

5 Answers5

38
func hashString (obj: AnyObject) -> String {
  return String(ObjectIdentifier(obj).uintValue)
}

let id = hashString(obj)

Swift 3.0

return String(UInt(ObjectIdentifier(obj))

Swift 4.1

return String(UInt(bitPattern: ObjectIdentifier(obj)))
Yogurt
  • 2,913
  • 2
  • 32
  • 63
aleclarson
  • 18,087
  • 14
  • 64
  • 91
  • 4
    Is it documented that hashValue of `ObjectIdentifier` is unique instead of beeing only a hash value? – Klaas Dec 04 '16 at 17:35
  • 1
    @Klaas Hash means it's not unique. Using of `ObjectIdentifier` is good, but using of hash value is wrong without more clue. Downvoted. – eonil Mar 04 '17 at 03:41
  • @Eonil Found this in the "swift-users" mailing list: https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20161003/003549.html – aleclarson Mar 04 '17 at 18:15
  • 1
    @aleclarson The message you cited mentions nothing about uniqueness. Basically, hashed value loses uniqueness of its original value by definition. – eonil Mar 04 '17 at 18:31
  • @Eonil A pointer address is always unique (at least until the memory is released). As long as the object is not released, you can be sure no other `ObjectIdentifier` will have the same `hashValue`. – aleclarson Mar 04 '17 at 20:41
  • 1
    @aleclarson Pointer address is unique, but `hashValue` doesn't have to be. It is just **based on** the pointer address, and it doesn't have to be equal with pointer value. By definition, `hashValue` is allowed to be duplicated for different object, and no one guarantees what the `hashValue` to be. – eonil Mar 04 '17 at 21:36
  • @Eonil The `hash` impl in [NSObject.swift](https://github.com/apple/swift-corelibs-foundation/blob/adbc558/Foundation/NSObject.swift#L193-L210) has this to say: "If two objects are equal (as determined by the `isEqual(_:)` method), they must have the same hash value." Notice that it uses `ObjectIdentifier(self).hashValue`. – aleclarson Mar 06 '17 at 00:12
  • 1
    @aleclarson Hash values don't have to be different for different source values. They're allowed to be duplicated without extra conditions. Which means it is not *unique*. – eonil Mar 14 '17 at 03:13
  • 1
    @aleclarson Also, you shouldn't depend on hidden implementation details. – eonil Mar 14 '17 at 03:14
  • 1
    @aleclarson: "2 equaled objects must have same hash value" doesn't mean "2 objects with same hash value must be equal". That's the first thing. Second thing is, even if 2 objects are equal, they still can be 2 different objects in memory. – namanhams Nov 21 '17 at 02:54
  • 1
    @aleclarson The answer is nice and I would like to upvote, but you should really remove the `.hashValue` variant for the reasons outlined. You can get the same hash for different OIDs by definition. This way it is just dangerous :-) – hnh Feb 12 '18 at 12:43
  • It's worth noting that [the current implementation](https://github.com/apple/swift/blob/8d745dcb6a833233bcd8d4e936c6ca310e5a51c6/stdlib/public/core/ObjectIdentifier.swift#L86-L106) of `ObjectIdentifier.hashValue` is identical to `.uintValue` or wrapping with `UInt`. They all use `Builtin.ptrtoint_Word`, but you're right that the `.hashValue` impl may change later and should not be relied upon. – aleclarson Feb 12 '18 at 13:07
8

How about a direct translation:

func pointerToString(objRef: NSObject) -> String {
  return NSString(format: "%p", objRef)
}

A more native way (in decimal, not hex):

func pointerToString(objRef: AnyObject) -> String {
  return withObjectAtPlusZero(objRef, { ptr in
    "\(UnsafePointer<RawByte>(ptr) - nil)"
  })
}

func pointerToString(objRef: AnyObject) -> String {
  let ptr: COpaquePointer =
    Unmanaged<AnyObject>.passUnretained(objRef).toOpaque()
  return "\(UnsafePointer<RawByte>(ptr) - nil)"
}

Update: Pointers stringify correctly now, so you can just do

func pointerToString(objRef: AnyObject) -> String {
  let ptr: COpaquePointer =
    Unmanaged<AnyObject>.passUnretained(objRef).toOpaque()
  return "\(ptr)"
}
newacct
  • 119,665
  • 29
  • 163
  • 224
  • if you are going to create an unmanaged object, shouldn't it be autoreleased? e.g. `let unman = Unmanaged.passUnretained(objRef).autorelease(); let ptr: COpaquePointer = unman.toOpaque()` – sketchyTech Sep 02 '14 at 12:03
  • @GoodbyeStackOverflow No, the underlying object is already managed so I don't think you want to introduce any more (duplicative) memory management of it. I think newacct's answer is correct as it stands. – Rob Oct 18 '14 at 21:11
4

Swift 4.1

String(UInt(bitPattern: ObjectIdentifier(obj)))

@aleclarson's answer update

func hashString(obj: AnyObject) -> String {
    return String(UInt(bitPattern: ObjectIdentifier(obj)))
}

let id = hashString(obj)
worriorbg
  • 351
  • 5
  • 14
0

what you could do to resolve the error is pretty much this:

    func pointerToString<T> (ptr: CMutablePointer<T>) -> String {
        // ...
    }

but printing the actual pointer is not really possible in Swift.

holex
  • 23,961
  • 7
  • 62
  • 76
-1

You can't. A native String in Swift is an object with a different and opaque memory layout than the contents of whatever memory is located at the CMutablePointer address. So you can't assign one to another. An alternative is to assign/copy an NSString that has been initialized with the contents of the memory at the pointer address.

hotpaw2
  • 70,107
  • 14
  • 90
  • 153