Check for Value or Reference Type in Swift

Check for value or reference type in Swift

AnyObject is a protocol that any class type automatically conforms to, so you can write:

func isReferenceType(toTest: Any) -> Bool {
return toTest.dynamicType is AnyObject
}

class Foo { }
struct Bar { }

isReferenceType(Foo()) // true
isReferenceType(Bar()) // false
isReferenceType("foo") // false
isReferenceType(123) // false
isReferenceType([1,2,3]) // false

Is there a way to check if a delegate is a value or a reference type?

Every class conforms to the AnyClass protocol. However enums and structs won't. Utilising that you can check if it's a class or a struct(or even an enum)

struct Test {
let delegate: Delegate

var isReferenceType: Bool {
return type(of:delegate) is AnyClass
}
}

Value type and reference type in Swift

First, NSString is immutable, so although it is a reference type, it cannot be changed.

Now, when you say var str:NSString = "Hello, playground" you are setting str as a reference to a constant string "Hello, playground".

You then have var newStr = str, so newStr and str will both refer to the same constant string.

Finally you have newStr = "hello", so newStr now refers to a different constant string. At no time did you modify the original constant string "Hello, playground", and indeed you can't since it is both a constant and an immutable class.

If, however, you use an NSMutableString and write:

var str:NSMutableString = NSMutableString(string:"Hello, playground")

var newStr = str

newStr.append(". Hello")

print(str)
print(newStr)

Then you will get the output

Hello, playground. Hello

Hello, playground. Hello

Since you are modifying the one object that is referenced by both variables.

How to get the reference of a value type in Swift?

There are several ways to get a reference. Kristopher's Box solution is one of the most flexible, and can be built as a custom box to handle problems like passing structs to ObjC.

Beyond that, the most obvious is passing inout parameters. This isn't precisely the same thing as a reference, but its behavior can be very similar, and definitely can be a part of high-performance code.

And moving down the stack there is UnsafePointer and its friends, using withUnsafePointer(to:) on general types, or .withUnsafeBufferPointer on array types.

But if you need a persistent reference type that can be stored in a property, you'll need a Box as Kristopher describes.

(Just to capture it for future readers since I hadn't remembered it, but MartinR pointed it out in a comment: as AnyObject can automatically box value types.)

Is a protocol either a reference or value type in Swift?


I don't think this should increase the reference count:

var transferServiceScanner: TransferServiceScanner increases the reference count to one, since all references are strong if they are not declared weak or sth else.

Storing the delegate variable as weak makes sure strong references do not go both ways and so ARC can deinit them.

I'm trying to figure out if a class were to simply conform to a protocol, does this create a reference?

A class is always a reference-type, whether you refer to it through a protocol or directly. So assigning a protocol with a reference-type(class) behind it does not copy the class-object, but you rather give out another reference to the object and increases the reference-count which ARC looks at.

With

protocol TransferServiceScannerDelegateProtocol: NSObjectProtocol {

you are making sure, that only a class can implement the protocol, that is why you can declare weak var delegate: TransferServiceScannerDelegateProtocol, because only classes can implement NSObjectProtocol with NSObject & co.

Without declaring a protocol class-only, either a struct or a class, both can implement the protocol. But only if you restrict the protocol to class-only can you use the protocol as if it were a class, using things like weak with it.

Check if `Any` value is object


UPDATE

The code I have shown below is reported as not working in release build.
(Please see Paul Cantrell's comment below.)

Apologies for my "as far as I tested" was too limited.

I'll update this answer when I find some further info about this.


I'm not sure we can see this behaviour in the next beta (or GM or Released version...), but this works as you expect in Xcode 8 beta 6.

let foo: Any = 4
if type(of: foo) is AnyClass {
print("It's an object.")
let object = foo as AnyObject
//do something with `object` that requires reference semantics
} else {
print("It's not an object.") //->It's not an object.
}

class MyClass {}
let bar: Any = MyClass()
if type(of: bar) is AnyClass {
print("It's an object.") //->It's an object.
let object = foo as AnyObject
//do something with `object` that requires reference semantics
} else {
print("It's not an object.")
}

let baz: Any = Array<AnyObject>()
if type(of: baz) is AnyClass {
print("It's an object.")
let object = foo as AnyObject
//do something with `object` that requires reference semantics
} else {
print("It's not an object.") //->It's not an object.
}

I cannot check all possible cases, so there may be some edge cases where this does not work. But as far as I tested, this seems to work as expected.

Why Swift protocol conforming values are treated as Value types by default?

Your protocol SampleProtocol could be adopted by a class or a struct. Swift is using the behavior of the value type which is the more restrictive type until you tell it that the protocol will only be used by a class reference type.

Add conformance to AnyObject to your protocol to get reference type behavior:

protocol SampleProtocol: AnyObject {
var message: String? { get set }
}

See The Swift 5.1 Programming Guide - Class-Only Protocols for more details.

The guide notes:

Use a class-only protocol when the behavior defined by that protocol’s
requirements assumes or requires that a conforming type has reference
semantics rather than value semantics.


Historical note: Using class keyword:

Prior to Swift 4.0, this was written using the class keyword:

protocol SampleProtocol: class {
var message: String? { get set }
}

This still works for the time being, but it is currently just a type alias for AnyObject and will likely be removed in a later version of Swift.

is tuple a value type or reference type in Swift?

A simple test demonstrates that tuples are value types:

var tuple1 = (1, 2)
var tuple2 = tuple1
tuple1.0 = 3
print("t1 = \(tuple1), t2 = \(tuple2)")

Output:

t1 = (3, 2), t2 = (1, 2)

If tuples were reference types, tuple2 would have been changed.



Related Topics



Leave a reply



Submit