I understand that Swift's tuples serve, for example, as a simple way for a function to return multiple values. However, beyond this "simplicity aspect", I don't see very well any necessity of using tuples instead of structs.
Therefore, my question: in terms of design, is there any scenario where tuples are clearly a better choice than structs?
This question of a slightly "discussion" nature, but I'll add two points in favour of sometimes preferring tuples over structures.
Native Equatable conformance for limited sized tuples
In Swift 2.2, tuples of up to size 6 will be natively equatable, given that it's members are equatable
Proposal SE-0015: Tuple comparison operators
This means tuples will sometimes be the natural choice over using smaller constructs in a limited scope.
E.g. consider the following example, using (1): a structure
struct Foo {
var a : Int = 1
var b : Double = 2.0
var c : String = "3"
}
var a = Foo()
var b = Foo()
// a == b // error, Foo not Equatable
/* we can naturally fix this by conforming Foo to Equatable,
but this needs a custom fix and is not as versatile as just
using a tuple instead. For some situations, the latter will
suffice, and is to prefer. */
func == (lhs: Foo, rhs: Foo) -> Bool {
return lhs.a == rhs.a && lhs.b == rhs.b && lhs.c == rhs.c
}
and (2): a tuple
/* This will be native in Swift 2.2 */
#warn_unused_result
public func == <A: Equatable, B: Equatable, C: Equatable>(lhs: (A,B,C), rhs: (A,B,C)) -> Bool {
return lhs.0 == rhs.0 && lhs.1 == rhs.1 && lhs.2 == rhs.2
}
/* end of native part ... */
var aa = (1, 2.0, "3")
var bb = (1, 2.0, "3")
aa == bb // true
aa.0 = 2
aa == bb // false
Generic access to different type tuples: more versatile than for different type structures
From the above (compare the == functions) it's also apparent that tuples are easily to work with in the context of generics, as we can access their anonymous member properties using the .0, .1 ... suffixes; whereas for a struct, the easiest way to mimic this behaviour quickly becomes quite complex, needing tools such as runtime introspection and so on, see e.g. this.
I don't know if anyone is still interested in this but sometimes i use tuples like this to keep things organised and it seems more readable to me.
struct SystemState{
let someSetting: Int
let someChoice: Int
let howmuchDoYouNeedForAnExample: String
}
struct UserState{
let name: String
let email: String
let accessLevel: Int
let blablabla:[String:String]
}
Given the above might be defined somewhere else:
typealias State = (system: SystemState,user: UserState)
let theSystemState = SystemState(someSetting: v1, someChoice: v2,
howmuchDoYouNeedForAnExample: v3)
let theUserState = theSystemState,UserState(name: v4, email: v5,
accessLevel: v6, blablabla: v7)
var state: State = (theSystemState,theUserState)
members are now accessed like this. It looks just the same as structs.
state.system.someSetting
state.user.accessLevel
As far as i can tell there is no real difference between using structs or tuples just the advantages mentioned in the accepted answer by dfri. Tuples also offer simplicity when you just want pass around something that is easier to keep together but has no need, or conceptually makes no sense, to be encapsulated in a struct. This way your code documents your intention in some small way that might make it easier to understand in the future for you or someone else.
Consider this, java to objective-C.
When you have to plug yourself to this type of datasource in your project (because you have a huge basecode in android and don't want or cannot do everything from scratch), you find yourself with java types datasource (like hashmap for example), those are basically typedef from objective-c types.
This is not easily plugable in swift depending on what you got, if you want to have a nice array for your collectionview, an array of tuples which will be filled by java datasource can be nice.
A little sample of code to illustrate this :
var myJavaVar = JavaUtilLinkedHashMap()
var headerArray : [(varName : String, param : Int, varId : jlong)] = []
myJavaVar = myStaticMethodToGetMyVar
// get var array
let keySetJava = myJavaVar.keySet().toArray()
for jCounter in 0..<Int(myJavaVar.size()){
// id for index
let key : Int = keySetJava.objectAtIndex(UInt(jCounter)) as! Int
let varId = jlong.init(key)
let varName = myMethodToGetName(varId)
let myParam : Int = myJavaVar.getWithId(keySetJava.objectAtIndex(UInt(jCounter))) as! Int
// append data to the array
headerArray.append((varName: categoryName, param : myParam,duration: duration, varId: varId))
}
You can then get your data like this (in your collectionview method):
let time = headerArray[indexPath.section].param
Related
I was thinking about how Swift ensures uniqueness for Set because I have turned one of my obj from Equatable to Hashable for free and so I came up with this simple Playground
struct SimpleStruct: Hashable {
let string: String
let number: Int
static func == (lhs: SimpleStruct, rhs: SimpleStruct) -> Bool {
let areEqual = lhs.string == rhs.string
print(lhs, rhs, areEqual)
return areEqual
}
}
var set = Set<SimpleStruct>()
let first = SimpleStruct(string: "a", number: 2)
set.insert(first)
So my first question was:
Will the static func == method be called anytime I insert a new obj inside the set?
My question comes from this thought:
For Equatable obj, in order to make this decision, the only way to ensure two obj are the same is to ask the result of static func ==.
For Hashable obj, a faster way is to compare hashValues... but, like in my case, the default implementation will use both string and number, in contrast with == logic.
So, in order to test how Set behaves, I have just added a print statement.
I have figured out that sometimes I got the print statement, sometimes no. Like sometimes hashValue isn't enough in order to make this decision ... So the method hasn't been called every time.
Weird...
So I've tried to add two objects that are equal and wondering what will be the result of set.contains
let second = SimpleStruct(string: "a", number: 3)
print(first == second) // returns true
set.contains(second)
And wonders of wonders, launching a couple of times the playground, I got different results and this might cause unpredictable results ...
Adding
var hashValue: Int {
return string.hashValue
}
it gets rid of any unexpected results but my doubt is:
Why, without the custom hashValue implementation, == sometimes gets called and sometimes it doesn't?
Should Apple avoid this kind of unexpected behaviours?
The synthesized implementation of the Hashable requirement uses all stored
properties of a struct, in your case string and number. Your implementation
of == is only based on the string:
let first = SimpleStruct(string: "a", number: 2)
let second = SimpleStruct(string: "a", number: 3)
print(first == second) // true
print(first.hashValue == second.hashValue) // false
This is a violation of a requirement of the Hashable protocol:
Two instances that are equal must feed the same values to Hasher in hash(into:), in the same order.
and causes the undefined behavior. (And since hash values are randomized
since Swift 4.2, the behavior can be different in each program run.)
What probably happens in your test is that the hash value of second is used to determine the “bucket” of the set in which the value
would be stored. That may or may not be the same bucket in which first is stored. – But that is an implementation detail: Undefined behavior is undefined behavior, it can cause unexpected results or even
runtime errors.
Implementing
var hashValue: Int {
return string.hashValue
}
or alternatively (starting with Swift 4.2)
func hash(into hasher: inout Hasher) {
hasher.combine(string)
}
fixes the rule violation, and therefore makes your code behave as expected.
This question already has answers here:
Perform assignment only if right side is not nil
(7 answers)
Closed 5 years ago.
Nil-Coalescing Operators are one of my favorite things about Swift. Since becoming quite familiar with Swift, I've run into a few different special cases. One is where I want to assign an Optional value to a variable if it exists, otherwise, do nothing. I currently see two ways of doing this:
var a : String?
var b : String?
// Possibly assign a non-nil value to a and/or b
/* First Way */
a = b ?? a
/* Second Way */
if let b = b {
a = b
}
In this context, it seems like the first way is probably fine, but when variables get much longer names like mapViewController.destinationCardTitle, the first way can get pretty long. I also can't just assign the value of a to nil or an empty String because if it already contains a non-nil value and b = nil, I don't want to change the value of a.
I'm wondering if there is a way to basically do the following without writing a twice.
a = b ?? a
You can design your own infix operator:
infix operator ?=
func ?=<T>(lhs: inout T, rhs: T?) {
lhs = rhs ?? lhs
}
var a = "a"
var b: String? = "b"
a ?= b
print(a) // "b\n"
Since your concern is with long variable names, let's look at your possibilities with long variable names:
nil-coalescing:
aLongVariableName = bCrazyLongVariableName ?? aLongVariableName
if-let:
if let x = bCrazyLongVariableName {
aLongVariableName = x
}
if-nil:
if bCrazyLongVariableName != nil {
aLongVariableName = bCrazyLongVariableName
}
Of those three possible options, if let is the shortest amount of code since each long variable name is only shown once. But you have the overhead of if let x = and the curly braces. So you need to have variable names that are long enough to counter that extra syntax.
Personally, readability is more important than length. And copy and paste means far less typing. For this reason, using ?? is the much better solution. Its intent is clearer.
You asked about a way to avoid the 2nd a using ??. You can't. There is no other syntax that provides the real shortcut you are looking for.
You could write a function that takes a as an inout parameter and b as a normal parameter.
func x(_ a: inout String?, _ b: String?) {
if b != nil {
a = b
}
}
var a = String?
var b = String?
x(&a, b)
It sure is interesting - and this is when you have a golden opportunity to make the most out of Swift. Use the big hammer on this one:
var a: String?
var b: String?
func attempt_To_Set_A_New_Value_To_A(using b: Any?) -> String {
if b != nil {
guard b is String else {
// OH no! It's not a string!!!
return ""
}
// Just to make sure..
if let bString = b as? String {
return bString
}
}
// WARNING - b is definitely nil!
return ""
}
a = attempt_To_Set_A_New_Value_To_A(using b: b).isEmpty() ? a : attempt_To_Set_A_New_Value_To_A(using b: b)
If you want to have less fun, or you're simply having a bad day, I'd probably write it as such:
if b != nil { a = b }
Why can't the === be used with String's in Swift? I am unable to compile the following:
let string1 = "Bob"
let string2 = "Fred"
if string1 === string2 {
...
}
and get the following error (on the if line):
Binary operator '===' cannot be applied to two 'String' operands
What I want to be able to do in my unit tests is, having performed a copyWithZone:, verify that two objects are indeed a different object with different pointers even if their values are the same. The following code doesn't work...
XCTAssertFalse(object1.someString === object2.someString)
If anyone knows of an alternative way please advise.
string1 and string2 are not NSString, but String. Since they are value objects, not reference objects, there is no reference that could be compared with ===.
Swift's === operator, by default, is only defined for classes.
Swift's String type is not a class but a struct. It does not inherit from AnyObject and therefore cannot be compared by reference.
You could of course implement an === operator for String in Swift, but I'm not sure how it would be any different from the implementation of == for Swift's String type.
func ===(lhs: String, rhs: String) -> Bool {
return lhs == rhs
}
Unless, of course, you really wanted to compare the references, I suppose you could do something like this:
func ===(lhs: String, rhs: String) -> Bool {
return unsafeAddressOf(lhs) == unsafeAddressOf(rhs)
}
However, for the sake of tests, rather than using the == or === operators, you should use the appropriate assertions:
XCTAssertEqual(foo, bar)
XCTAssertNotEqual(foo, bar)
The === operator is the identity operator. It checks if two variables or constants refer to the same instance of a class. Strings are not classes (they are structs) so the === operator does not apply to them.
If you want to check if two strings are the same, use the equality operator == instead.
Read all about the identity operator in the Swift documentation.
You can just check two objects for identity directly, instead of checking a property of type String.
XCTAssertFalse(object1 === object2)
Swift Strings are value type, not reference type, so there's no need for that, a copy will always be a different object.
You should just compare by value with ==.
If you try really hard, you can force things to happen, but I'm not sure what that buys you.
class MyClass: NSObject, NSCopying {
var someString: NSString = ""
required override init() {
super.init()
}
func copyWithZone(zone: NSZone) -> AnyObject {
let copy = self.dynamicType.init()
copy.someString = someString.copy() as? NSString ?? ""
return copy
}
}
let object1 = MyClass()
object1.someString = NSString(format: "%d", arc4random())
let object2 = object1.copy()
if object1.someString === object2.someString {
print("identical")
} else {
print("different")
}
prints identical, the system is really good at conserving strings.
I'm working with a C API from Swift and for one of the methods that I need to call I need to give a
UnsafeMutablePointer<UnsafeMutablePointer<UnsafeMutablePointer<Int8>>>
More Info:
Swift Interface:
public func presage_predict(prsg: presage_t, _ result: UnsafeMutablePointer<UnsafeMutablePointer<UnsafeMutablePointer<Int8>>>) -> presage_error_code_t
Original C:
presage_error_code_t presage_predict(presage_t prsg, char*** result);
Generally, if a function takes a UnsafePointer<T> parameter
then you can pass a variable of type T as in "inout" parameter with &. In your case, T is
UnsafeMutablePointer<UnsafeMutablePointer<Int8>>
which is the Swift mapping of char **. So you can call the C function
as
var prediction : UnsafeMutablePointer<UnsafeMutablePointer<Int8>> = nil
if presage_predict(prsg, &prediction) == PRESAGE_OK { ... }
From the documentation and sample code of the Presage library I
understand that this allocates an array of strings and assigns the
address of this array to the variable pointed to by prediction.
To avoid a memory leak, these strings have to be released eventually
with
presage_free_string_array(prediction)
To demonstrate that this actually works, I have taken the first
part of the demo code at presage_c_demo.c and translated it
to Swift:
// Duplicate the C strings to avoid premature deallocation:
let past = strdup("did you not sa")
let future = strdup("")
func get_past_stream(arg: UnsafeMutablePointer<Void>) -> UnsafePointer<Int8> {
return UnsafePointer(past)
}
func get_future_stream(arg: UnsafeMutablePointer<Void>) -> UnsafePointer<Int8> {
return UnsafePointer(future)
}
var prsg = presage_t()
presage_new(get_past_stream, nil, get_future_stream, nil, &prsg)
var prediction : UnsafeMutablePointer<UnsafeMutablePointer<Int8>> = nil
if presage_predict(prsg, &prediction) == PRESAGE_OK {
for var i = 0; prediction[i] != nil; i++ {
// Convert C string to Swift `String`:
let pred = String.fromCString(prediction[i])!
print ("prediction[\(i)]: \(pred)")
}
presage_free_string_array(prediction)
}
free(past)
free(future)
This actually worked and produced the output
prediction[0]: say
prediction[1]: said
prediction[2]: savages
prediction[3]: saw
prediction[4]: sat
prediction[5]: same
There may be a better way but this runs in playground and defines a value r with the type you want:
func ptrFromAddress<T>(p:UnsafeMutablePointer<T>) -> UnsafeMutablePointer<T>
{
return p
}
var myInt:Int8 = 0
var p = ptrFromAddress(&myInt)
var q = ptrFromAddress(&p)
var r = ptrFromAddress(&q)
What's the point of defining ptrFromAddress, which seems like it does nothing? My thinking is that the section of the Swift interop book which discusses mutable pointers shows many ways to initialize them by passing some expression as an argument (like &x), but does not seem to show corresponding ways where you simply call UnsafeMutablePointer's initializer. So let's define a no-op function just to use those special initialization methods based on argument-passing
Update:
While I believe the method above is correct, it was pointed out by #alisoftware in another forum that this seems to be a safer and more idiomatic way to do the same thing:
var myInt: Int8 = 0
withUnsafeMutablePointer(&myInt) { (var p) in
withUnsafeMutablePointer(&p) { (var pp) in
withUnsafeMutablePointer(&pp) { (var ppp) in
// Do stuff with ppp which is a UnsafeMutablePointer<UnsafeMutablePointer<UnsafeMutablePointer<Int8>>>
}
}
}
It's more idiomatic because you're using the function withUnsafeMutablePointer which is supplied by the Swift standard library, rather than defining your own helper. It's safer because you are guaranteed that the UnsafeMutablePointer is only alive during the extent of the call to the closure (so long as the closure itself does not store the pointer).
I'm doing some performance testing of Swift vs Objective-C.
I created a Mac OS hybrid Swift/Objective-C project that creates large arrays of prime numbers using either Swift or Objective-C.
It's got a decent UI and shows the results in a clear display. You can check out the project on Github if you're interested. It's called SwiftPerformanceBenchmark.
The Objective-C code uses a malloc'ed C array of ints, and the Swift code uses an Array object.
The Objective C code is therefore a lot faster.
I've read about creating an Array-like wrapper around a buffer of bytes using code like this:
let size = 10000
var ptr = UnsafePointer<Int>malloc(size)
var bytes = UnsafeBufferPointer<Int>(start: ptr, count: data.length)
I'd like to modify my sample program so I can switch between my Array<Int> storage and using an UnsafeBufferPointer<Int> at runtime with a checkbox in the UI.
Thus I need a base type for my primes array that will hold either an Array<Int> or an UnsafeBufferPointer<Int>. I'm still too weak on Swift syntax to figure out how to do this.
For my Array- based code, I'll have to use array.append(value), and for the UnsafeBufferPointer<Int>, which is pre-filled with data, I'll use array[index]. I guess if I have to I could pre-populate my Array object with placeholder values so I could use array[index] syntax in both cases.
Can somebody give me a base type that can hold either an Array<Int> or an UnsafeBufferPointer<Int>, and the type-casts to allocate either type at runtime?
EDIT:
Say, for example, I have the following:
let count = 1000
var swiftArray:[Int]?
let useSwiftArrays = checkbox.isChecked
typealias someType = //A type that lets me use either unsafeArray or swiftArray
var primesArray: someType?
if useSwiftArrays
{
//Create a swift array version
swiftArray [Int](count: count, repeatedValue: 0)
primesArray = someType(swiftArray)
}
else
{
var ptr = UnsafePointer<Int>malloc(count*sizeof(Int))
var unsafeArray = UnsafeBufferPointer<Int>(start: ptr, count: data.length)
primesArray = someType(unsafeArray)
}
if let requiredPrimes = primesArray
{
requiredPrimes[0] = 2
}
#MartinR's suggestion should help get code that can switch between the two. But there's a shortcut you can take to prove whether the performance difference is between Swift arrays and C arrays, and that's to switch the Swift compiler optimization to -Ounchecked. Doing this eliminates the bounds checks on array indices etc that you would be doing manually by using unsafe pointers.
If I download your project from github and do that, I find that the Objective-C version is twice as fast as the Swift version. But... that’s because sizeof(int) is 4, but sizeof(Int) is 8. If you switch the C version to use 8-byte arithmetic as well...
p.s. it works the other way around as well, if I switch the Swift code to use UInt32, it runs at 2x the speed.
OK, it’s not pretty but here is a generic function that will work on any kind of collection, which means you can pass in either an Array, or an UnsafeMutableBufferPointer, which means you can use it on a malloc’d memory range, or using the array’s .withUnsafeMutableBufferPointer.
Unfortunately, some of the necessities of the generic version make it slightly less efficient than the non-generic version when used on an array. But it does show quite a nice performance boost over arrays in -O when used with a buffer:
func storePrimes<C: MutableCollectionType where C.Generator.Element: IntegerType>(inout store: C) {
if isEmpty(store) { return }
var candidate: C.Generator.Element = 3
var primeCount = store.startIndex
store[primeCount++] = 2
var isPrime: Bool
while primeCount != store.endIndex {
isPrime = true
var oldPrimeCount = store.startIndex
for oldPrime in store {
if oldPrimeCount++ == primeCount { break }
if candidate % oldPrime == 0 { isPrime = false; break }
if candidate < oldPrime &* oldPrime { isPrime = true; break }
}
if isPrime { store[primeCount++] = candidate }
candidate = candidate.advancedBy(2)
}
}
let totalCount = 2_000_000
var primes = Array<CInt>(count: totalCount, repeatedValue: 0)
let startTime = CFAbsoluteTimeGetCurrent()
storePrimes(&primes)
// or…
primes.withUnsafeMutableBufferPointer { (inout buffer: UnsafeMutableBufferPointer<CInt>) -> Void in
storePrimes(&buffer)
}
let now = CFAbsoluteTimeGetCurrent()
let totalTime = now - startTime
println("Total time: \(totalTime), per second: \(Double(totalCount)/totalTime)")
I am not 100% sure if I understand your problem correctly, but perhaps
this goes into the direction that you need.
Both Array and UnsafeMutablePointer conform to MutableCollectionType (which requires a subscript getter and setter).
So this function would accept both types:
func foo<T : MutableCollectionType where T.Generator.Element == Int, T.Index == Int>(inout storage : T) {
storage[0] = 1
storage[1] = 2
}
Example with buffer pointer:
let size = 2
var ptr = UnsafeMutablePointer<Int>(malloc(UInt(size * sizeof(Int))))
var buffer = UnsafeMutableBufferPointer<Int>(start: ptr, count: size)
foo(&buffer)
for elem in buffer {
println(elem)
}
Example with array:
var array = [Int](count: 2, repeatedValue: 0)
foo(&array)
for elem in array {
println(elem)
}
For non-mutating functions you can use CollectionType
instead of MutableCollectionType.