Difference Between Using Objectidentifier() and '===' Operator

Difference between using ObjectIdentifier() and '===' Operator

There is no difference for class instance, see the following
comments in ObjectIdentifier.swift:

  /// Creates an instance that uniquely identifies the given class instance.
///
/// The following example creates an example class `A` and compares instances
/// of the class using their object identifiers and the identical-to
/// operator (`===`):
///
/// class IntegerRef {
/// let value: Int
/// init(_ value: Int) {
/// self.value = value
/// }
/// }
///
/// let x = IntegerRef(10)
/// let y = x
///
/// print(ObjectIdentifier(x) == ObjectIdentifier(y))
/// // Prints "true"
/// print(x === y)
/// // Prints "true"
///
/// let z = IntegerRef(10)
/// print(ObjectIdentifier(x) == ObjectIdentifier(z))
/// // Prints "false"
/// print(x === z)
/// // Prints "false"
///

It also becomes apparent from the
implementation of == for ObjectIdentifier,
which just compares the pointers to the object storage:

  public static func == (x: ObjectIdentifier, y: ObjectIdentifier) -> Bool {
return Bool(Builtin.cmp_eq_RawPointer(x._value, y._value))
}

which is what the === operator
does as well:

public func === (lhs: AnyObject?, rhs: AnyObject?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return Bool(Builtin.cmp_eq_RawPointer(
Builtin.bridgeToRawPointer(Builtin.castToUnknownObject(l)),
Builtin.bridgeToRawPointer(Builtin.castToUnknownObject(r))
))
case (nil, nil):
return true
default:
return false
}
}

ObjectIdentifier conforms to Hashable, so it is useful if you want to implement that protocol for your class:

extension MyClass: Hashable {
var hashValue: Int {
return ObjectIdentifier(self).hashValue
}
}

An object identifier can also be created for meta types
(e.g. ObjectIdentifier(Float.self)) for which === is not defined.

Difference between == and ===

In short:

== operator checks if their instance values are equal, "equal to"

=== operator checks if the references point the same instance, "identical to"

Long Answer:

Classes are reference types, it is possible for multiple constants and variables to refer to the same single instance of a class behind the scenes. Class references stay in Run Time Stack (RTS) and their instances stay in Heap area of Memory. When you control equality with == it means if their instances are equal to each other. It doesn't need to be same instance to be equal. For this you need to provide a equality criteria to your custom class. By default, custom classes and structures do not receive a default implementation of the equivalence operators, known as the “equal to” operator == and “not equal to” operator != . To do this your custom class needs to conform Equatable protocol and it's static func == (lhs:, rhs:) -> Bool function

Let's look at example:

class Person : Equatable {
let ssn: Int
let name: String

init(ssn: Int, name: String) {
self.ssn = ssn
self.name = name
}

static func == (lhs: Person, rhs: Person) -> Bool {
return lhs.ssn == rhs.ssn
}
}

P.S.: Since ssn(social security number) is a unique number, you don't need to compare if their name are equal or not.

let person1 = Person(ssn: 5, name: "Bob")
let person2 = Person(ssn: 5, name: "Bob")

if person1 == person2 {
print("the two instances are equal!")
}

Although person1 and person2 references point two different instances in Heap area, their instances are equal because their ssn numbers are equal. So the output will be the two instance are equal!

if person1 === person2 {
//It does not enter here
} else {
print("the two instances are not identical!")
}

=== operator checks if the references point the same instance, "identical to". Since person1 and person2 have two different instance in Heap area, they are not identical and the output the two instance are not identical!

let person3 = person1

P.S: Classes are reference types and person1's reference is copied to person3 with this assignment operation, thus both references point the same instance in Heap area.

if person3 === person1 {
print("the two instances are identical!")
}

They are identical and the output will be the two instances are identical!

ObjectIdentifier needed for Swift equality?

Why is ObjectIdentifier needed, and when should it be used for object equality?

You don't need to use ObjectIdentifier in order to perform an identity comparison in this case, you can simply use the identity operator === instead which, as Martin says here, for class instances is equivalent to using ObjectIdentifier's == overload:

func check(testInstance: CustomClass) -> Bool {
return array1.contains(where: { $0 === testInstance })
}

Also note we're using contains(where:) over filter{...}.count > 0, as the former will short-circuit upon finding a matching element, whereas the latter evaluates the entire sequence (and creates an unnecessary intermediate array).

The direct use of == to perform an identity comparison of objects may have worked due to the fact that CustomClass ultimately inherits from NSObject, which conforms to Equatable by defining an == overload that calls isEqual(_:), which by default performs an identity comparison.

However in general, this should not be relied upon – the implementation of isEqual(_:) can be overridden to perform a comparison based on property values rather than identity. Furthermore, semantically Equatable requires that the implementation of == is based all on visible aspects (i.e property values) of the instances being compared.

From the documentation:

Equality implies substitutability — any two instances that compare
equally can be used interchangeably in any code that depends on their
values. To maintain substitutability, the == operator should take into
account all visible aspects of an Equatable type.

Therefore using == for an identity comparison on your class was never correct, even though it may have worked initially.

As for when ObjectIdentifier should be used, really you should never need it just to perform an identity comparison. For classes, you should use the === operator, and for metatypes, you should simply use the == overload defined specifically for them (as in that case, identity just happens to equality, as each new metatype instance is unique).

The main use of ObjectIdentifier is really its hashValue implementation, which is derived from the pointer value of the thing that it's initialised with. This can be useful, for example, in allowing metatypes to be Dictionary keys (compare Make a Swift dictionary where the key is "Type"?).

Is there a difference between is and isKindOfClass()?

Yes there is a difference: is works with any class in Swift, whereas isKindOfClass() works only with those classes that are subclasses of NSObject or otherwise implement NSObjectProtocol.

How to remove specific object from array?

  1. Is there a less complicated way to do this?

You could omit self when accessing the delegates member, and bake the resulting index of the index(where:) call into a call to the map method of Optional:

func removeDelegate(_ delegate: MyDelegate) {
delegates.index(where: { $0 == delegate})
.map { delegates.remove(at: $0) }
}

In case no such delegate object is found, the expression above simply results nil (non-captured result).


This conditional, "{ $0 == delegate }", is giving causing the error,
"Cannot convert value of type '(OptionalNilComparisonType) -> Bool' to
expected argument type '() -> Bool'". How can I fix this? I've tried
adding ? and ! but still not fully understanding Swift's optional
concept.

This is yet another example of a kind of obscure error message in Swift. The core error is that MyDelegate does not have an == operator defined (does not conform to Equatable).

After your edit you showed, however, that MyDelegate is a protocol, so if you let this conform to Equatable, you wont be able to (as it will be contain a Self type requirement) use MyDelegate as a concrete type (only as e.g. type constraint on a generic).

If your concrete delegate objects are reference ones (class), and you want to test object equality in the sense of testing if both refer to the same object (object reference), you could make use of the the Object​Identifier available to class instances. Constraining MyDelegate (after your edit you showed it to be a protocol) to only classes,

protocol MyDelegate: class { /* ... */ }

and testing the equality of the ObjectIdentifier in the index(where:) call above:

func removeDelegate(_ delegate: MyDelegate) {
delegates.index(where: { ObjectIdentifier($0) == ObjectIdentifier(delegate) })
.map { delegates.remove(at: $0) }
}

Looking at the source code for ObjectIdentifier, we see that this will compare the underlying raw ptr values of two delegate instances; from swift/stdlib/public/core/ObjectIdentifier.swift:

public static func == (x: ObjectIdentifier, y: ObjectIdentifier) -> Bool {
return Bool(Builtin.cmp_eq_RawPointer(x._value, y._value))
}

As mentioned by @MartinR in the comments to the question above, rather than going via ObjectIdentifier, you could also use the === identity operator for class instances directly.

func removeDelegate(_ delegate: MyDelegate) {
delegates.index(where: { $0 === delegate })
.map { delegates.remove(at: $0) }
}

For completeness, we may verify that === make use of the same Builtin method as the == operator for ObjectIdentifier, by having a look at swift/stdlib/public/core/Equatable.swift:

public func === (lhs: AnyObject?, rhs: AnyObject?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return Bool(Builtin.cmp_eq_RawPointer(
Builtin.bridgeToRawPointer(Builtin.castToUnknownObject(l)),
Builtin.bridgeToRawPointer(Builtin.castToUnknownObject(r))
))
case (nil, nil):
return true
default:
return false
}
}

What's the difference between typeof foo and typeof(foo) / delete bar and delete(bar), and why do we need both?

Both typeof and delete are unary operators but with a big difference between the two: typeof evaluates its operand and delete does not. Operator precedence (MDN reference) is useful for discussion.

delete UnaryExpression

The delete operator does not evaluate its operand and ignores grouping operators '(' and ')' around it because they have higher precedence than delete. Hence delete could never be written as function call using the same syntax because a function call would evaluate function arguments. Delete can use property short cut or lookup syntax:

delete objectIdentifier.property name;  // is equivalent to
delete objectIdentifier["propertyName"]

In strict mode a syntax error is generated if you try using delete on anything that is not a reference to an object property that includes both the property name and object.

typeof UnaryExpession

The typeof operator evaluates its operand. If the operand expression is enclosed in parentheses, the entire expression between the parentheses is evaluated before applying typeof to the result. Using parentheses around an operator that can be identified using rules for operator precedence alone is redundant.

Summary

Using parentheses around an operand for delete doesn't help and is probably best avoided.

Use parentheses around a typeof operand to obtain the type of an expression result.

Placing parentheses around typeof and its operand, as in (typeof operand) can be useful in debugging a complex expression if operator precedence or automatic type conversions are not working as expected.

The use of parentheses around operands for delete and typeof does not create two kinds of syntax for the operators - the parentheses follow normal rules for parsing an expression.

What is the differences between 'identifier' and 'reference' concepts in PHP programming language?

Let us start with the last part of your quotation.

And, when an object is sent by argument, returned or assigned to
another variable, the different variables are not aliases: they hold a
copy of the identifier, which points to the same object.

Object Identifiers

In general, an object identifier is an integer value that identifies an object. Let us create some objects from a bare class A. We would use SPL's spl_object_id() and var_dump() functions.

Both functions return the object handle for the given object. The object handle is not a memory address.

class A {}

$a = new A();
$b = $a;

var_dump($a) . PHP_EOL;
var_dump($b) . PHP_EOL;

// object(A)#1 (0) { Notice #1 - an identifier
// }

// object(A)#1 (0) { Notice #1 - an identifier
// }

echo spl_object_id($a) . PHP_EOL; // Outputs: 1 - an identifier
echo spl_object_id($b) . PHP_EOL; // Outputs: 1 - an identifier

$a and $b hold a copy of the identifier, which points to the same object (A).

If a PHP script created 500 objects, then each object id would be unique for the lifetime of the object. Each object id can be used as a key for storing objects, or for identifying an object, as long as the object is not destroyed/garbage collected. Once the object is destroyed, its id may be reused for other objects.

Now, let us start with the first part of your quotation.

References in PHP are a means to access the same variable content by
different names and they are not actual memory addresses. Instead,
they are symbol table aliases.

Reference

In PHP, reference has a different meaning. Therefore, it allows you to access a value with different variable names. A reference is created by the & operator in PHP:

$a = 12;

// Notice the & (ampersand)
$b = & $a; // $b = 12;

$b = 20; // $a = 20; now

Here we can access the value 20 using $a and $b. Ok! We now need to know what happens when we assign a value to a variable because you quoted that the different variable names are not actually memory address. Let us dig into that.

zval Container

A PHP variable is stored in a container called a zval. A zval container is created when a new variable is created with a constant value, such as:

$a = "hello";

A zval container stores four types of information about a variable:

  • Type - variable's type
  • Value - variable's value
  • is_ref - a boolean value representing whether or not the variable is part of a "reference set"
  • refcount - holds how many variable names (also called symbols) point to this one zval container

There is a function named xdebug_debug_zval() which is available when Xdebug is installed; It helps you dig into how a variable with a value resides in a zval container.

$a = "hello";
xdebug_debug_zval('a');

This outputs as the following:

a: (refcount=1, is_ref=0)='hello'

Or graphically you can imagine the zval container as the following:

Sample Image

Symbol Table

The zval container does not include variable names. Those are stored in what is called a symbol table. In the symbol table our "Reference" section's exmaple looks like:

symbol | value
-------+------
a, b | 20

So a and b symbols are aliases here. This happens for the scalar types only.

There are four types of scope in PHP - local, global, static, and function parameters. There is a symbol table for each level of scope. As opposed to scalar values, arrays and objects store their properties in a symbol table of their own. In the symbol table, our "Object Identifiers" section's example looks like:

$a = new A();
$b = $a;

symbol | value object | details
-------+--------- -------------+--------
a | object(A)#1 object(A)#1 | class A { ... }
b | object(A)#1

Here a and b symbols are not aliases. They hold a copy of the identifier, which points to the same object.

References:

  1. https://www.php.net/manual/en/features.gc.refcounting-basics.php
  2. http://www.levijackson.net/php-zvals-and-symbol-tables/
  3. Objects and references in php 5
  4. https://github.com/php/php-src/pull/2611
  5. https://github.com/php/php-src/commit/5097e2ee13de12b4445b4123e1554c0733c6853c

Reference as key in swift dictionary

Equality can be implemented as object identity, i.e. a == b iff a and b refer to the same instance of the class, and the hash value can be build from the ObjectIdentifier (which is the same for identical objects, compare e.g. Difference between using ObjectIdentifier() and '===' Operator):

For Swift 4.2 and later:

class Test : Hashable {
static func ==(lhs: Test, rhs: Test) -> Bool {
return lhs === rhs
}

public func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self))
}
}

For Swift 3:

class Test : Hashable {
var hashValue: Int { return ObjectIdentifier(self).hashValue }
}

func ==(lhs: Test, rhs: Test) -> Bool {
return lhs === rhs
}

For Swift 2.3 and earlier, you can use

/// Return an UnsafePointer to the storage used for `object`.  There's
/// not much you can do with this other than use it to identify the
/// object
func unsafeAddressOf(object: AnyObject) -> UnsafePointer<Void>

i.e.

class Test : Hashable {
var hashValue: Int { return unsafeAddressOf(self).hashValue }
}

func ==(lhs: Test, rhs: Test) -> Bool {
return lhs === rhs
}

Example:

var dictionary = [Test: String]()
let a = Test()
let b = Test()
dictionary[a] = "A"
print(dictionary[a]) // Optional("A")
print(dictionary[b]) // nil

implement the Equatable protocol.



Related Topics



Leave a reply



Submit