Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Array memory address changes when referring to the same variable

I was learning Python and wanted to confirm a certain behavior in Objective-C and Swift.

The test was as follows:

Python

def replace(list):
    list[0] = 3
    print(list)

aList = [1, 2, 3]
print(aList)
replace(aList)
print(aList)

Objective-C

- (void)replace:(NSMutableArray *)array {
    array[0] = @1;
    NSLog(@"array: %@, address: %p\n%lx", array, array, (long)&array);
}

NSMutableArray *array = [@[@1, @2, @3] mutableCopy];
NSLog(@"original: %@, address: %p \n%lx", array, array, (long)&array);
[self replace:array];
NSLog(@"modified: %@, address: %p \n%lx", array, array, (long)&array);

Swift

var numbers = [1, 2, 3]
let replace = { (var array:[Int]) -> Void in
    array[0] = 2
    print("array: \(array) address:\(unsafeAddressOf(array as! AnyObject))")
}

print("original: \(numbers) address:\(unsafeAddressOf(numbers as! AnyObject))")
replace(numbers)
print("modified: \(numbers) address:\(unsafeAddressOf(numbers as! AnyObject))")

All the results came out as expected except for the address part in Swift. In Objective-C, the address of array remained the same in original and modified, but the print result of Swift was:

original: [1, 2, 3] address:0x00007f8ce1e092c0
array: [2, 2, 3] address:0x00007f8ce1f0c5d0
modified: [1, 2, 3] address:0x00007f8ce4800a10

Is there something I'm missing?

like image 516
funct7 Avatar asked Mar 14 '23 21:03

funct7


1 Answers

Arrays in Swift have value semantics, not the reference semantics of arrays in Python and Objective-C. The reason you're seeing different addresses (and addresses at all) is that every time you do an as! AnyObject cast, you're actually telling Swift to bridge your Array<Int> struct to an instance of NSArray. Since you bridge three times, you get three different addresses.


You shouldn't need to think about the address of a Swift array, but if you'd like to (momentarily) get the address of an array's buffer, you can do it this way:

func getBufferAddress<T>(array: [T]) -> String {
    return array.withUnsafeBufferPointer { buffer in
        return String(reflecting: buffer.baseAddress)
    }
}

This lets you see the buffer's copy-on-write in action:

var numbers = [1, 2, 3]
let numbersCopy = numbers

// the two arrays share a buffer here
print(getBufferAddress(numbers))                 // "0x00007fba6ad16770"
print(getBufferAddress(numbersCopy))             // "0x00007fba6ad16770"

// mutating `numbers` causes a copy of its contents to a new buffer
numbers[0] = 4

// now `numbers` has a new buffer address, while `numbersCopy` is unaffected
print(getBufferAddress(numbers))                 // "0x00007ff23a52cc30"
print(getBufferAddress(numbersCopy))             // "0x00007fba6ad16770"
like image 131
Nate Cook Avatar answered Apr 26 '23 05:04

Nate Cook