This post written on August 23, 2015 to be compatible with Xcode 7 Beta and Swift 2
Objective-C Pointers and Swift
Reading C pointers from Swift
Let’s say I have a C Int object as a Swift pointer.
For example this Objective-C method would return an int pointer, or (int *) in C terminology:
@interface
PointerBridge
:
NSObject
{
int count;
}
- (int *) getCountPtr;
@end
@implementation
PointerBridge
- (instancetype)
init
{
self
= [
super
init
];
if
(
self
) {
count = 23;
}
return
self
;
}
- (int *) getCountPtr {
return
&count;
}
@end
If you’re unfamiliar with Obj-C or C syntax, the above basically just declares a class called PointerBridge, and it has a single method called getCountPtr, which returns the memory address of an int whose value is 23. The Int is just a variable on the instance called count, which gets set to 23 in the constructor, init.
I put this in an Objective-C h-file and import it in to my bridging header in order to expose it to Swift. Then in Swift I create the PointerBridge class as a new instance called bridge, and get the result of the getCountPtr() method…
let
bridge =
PointerBridge
()
let
theInt = bridge.getCountPtr()
print
(theInt)
print
(theInt.memory)
Examine the type of theInt by holding the Option key and clicking the variable name in Xcode, and you’ll find it’s Swift type is a UnsafeMutablePointer<Int32>. This is a pointer to an Int, which is not the same thing as being an Int… It just points to it.
If we run this small program and allow this Swift to execute, we’ll see theInt printed to the console as a memory address, something like 0x00007f8bdb508ef8. Then, we’ll see the value 23 printed from the memory member. Accessing memory on a pointer generally returns the underlying object, in this case just the original 32-bit int (brought in to Swift as an Int32)
Now consider our Objective-C class also allows us to set the value of count.
@interface
PointerBridge
:
NSObject
{
int count;
}
- (int *) getCountPtr;
- (void) setCount:(int)newCount;
@end
@implementation
PointerBridge
- (instancetype)
init
{
self
= [
super
init
];
if
(
self
) {
count = 23;
}
return
self
;
}
- (int *) getCountPtr {
return
&count;
}
- (void) setCount:(int)newCount {
count = newCount;
}
@end
Now, we could modify the value for count by calling setCount(). Since theInt is a pointer, updating the count value using setCount should also update theInt.memory. The memory address won’t change after all, just the value.
In other words, the following should log to the console the number 23, and then the number 1000.
let
bridge =
PointerBridge
()
let
theInt = bridge.getCountPtr()
print
(theInt.memory)
// 23
bridge.setCount(1000)
print
(theInt.memory)
// 1000
A shortcut you might want to take, to avoid writing .memory all the time, is to assign .memory to a variable:
let
bridge =
PointerBridge
()
let
theInt = bridge.getCountPtr()
let
countVal = theInt.memory
print
(countVal)
// 23
As before, 23 is printed to the console. However, if I do the same thing we did before, and modify the underlying count value by calling setCount(), we see a problem crop up:
let
bridge =
PointerBridge
()
let
theInt = bridge.getCountPtr()
let
countVal = theInt.memory
print
(countVal)
// 23
bridge.setCount(1000)
print
(countVal)
// 23
What’s happening here is that countVal is being assigned by value. At the time of assignment the value is 23, so countVal actually has it’s own memory address where it stores 23 permanently, thus losing it’s pointer nature. countVal is just a regular Int32 now.
Creating C pointers from Swift Objects
What if we wanted to do the inverse of what we did above? Instead of setting the value of count with an Int, what if we needed to pass in a pointer?
So in the Objective-C let’s say there was a method like this:
- (void) setCountPtr:(int *)newCountPtr {
count = *newCountPtr;
}
This method is pretty contrived, and just ends up re-assigning the value of the newCountPtr, but in your Swift development you will face real-world scenarios of needing to pass in pointers. This one is contrived simply to demonstrate how to create the pointer type from Swift, and pass it in.
You might think you could simply pass in an Int value, with a reference operator like &. This is how you might do this same thing in C. Or for example in Objective-C you would write this:
int mcount = 500;
[
self
setCountPtr:&mcount];
Which would update the count value to be 500 successfully. However in Swift, you may see just from auto-complete that it wants something a little more complicated (and verbose). It’s looking for a newCountPtr of type UnsafeMutablePointer<Int32>.
I know this type is a mouthful, and it looks really complicated. However, it’s actually fairly simple, especially if you know pointers from Obj-C. In order to create an object with type UnsafeMutablePointer<Int32>, we just call the constructor and for it’s only argument we pass in the size of the pointer in units of the number of objects.
let
bridge =
PointerBridge
()
let
theInt = bridge.getCountPtr()
print
(theInt.memory)
// 23
let
newIntPtr =
UnsafeMutablePointer
<
Int32
>.alloc(1)
newIntPtr.memory = 100
bridge.setCountPtr(newIntPtr)
print
(theInt.memory)
// 100
The only argument to the UnsafeMutablePointer<Int32> constructor is how many objects worth of space will be allocated, so we can just pass in 1 since we only need 1 Int32 object. Then, simply inverting what we did before with the memory property, we can assign a value to our newly created pointer. Finally, we can simply pass in the newIntPtr to the setCountrPtr method. Printing out the value of the original theInt pointer, we see the value has indeed been updated to 100.
Conclusion
The UnsafeMutablePointer<T> type, and it’s sibling UnsafePointer<T> are basically just abstractions of C pointers. If it helps to wrap your head around them, think of them sort of like Swift Optionals, in that they do not directly equate to a certain value, but instead act as an abstraction layer on top of a contained variable. The type is generic, and allows for other types to be used than just Int32. For example if you needed to work with a Float object you might need an UnsafeMutablePointer<Float>.
The important thing to remember is that you do not cast from an Int to an UnsafeMutablePointer<Int>, because a pointer is simply not the same as an Int value. So instead, be prepared to create a new object, calling it’s constructor UnsafeMutablePointer<Int>(count: Int)
.
We’ll follow-up this post with a deeper dive in to the details of function pointers, and how to take advantage of these features to work with legacy C and Objective-C APIs. Make sure to sign up for our newsletter so you don’t miss it!