I am learning swift and am quite surprised at a paradigm, I would like to better understand, about initialization. (please note I am not a professional developer).
In the following code, I get an error at build time on the last line :
Variable 'ex1' used before being initialized
class Example {
var data:Int
init (value: Int){
self.data = value
}
}
var ex1: Example
var ex2: Example
for i in 1...2 {
switch i {
case 1:
ex1 = Example(value: 10)
case 2 :
ex2 = Example(value: 20)
default:
break
}
}
var result = ex1
So, my understanding is that the compiler is not smart enough to understand that ex1 is initialized at execution in the "for" loop before.
Ok fine. So now I have to initialize my variables a priori, to a random value. This seems odd to me, as not initializing them feels as a safety to spot errors.
Now I have to carry a possibly wrong value without warning. Is this the good practice ? Am I missing something ?
It is possible to declare a variable without initializing it immediately and without the compiler complaining. For example:
let ex: Example
if someBoolean {
ex = Example(value: 10)
} else {
ex = Example(value: 20)
}
print(ex) // this should work fine
However, the compiler only knows how to handle this if all declared variables are initialized in all logical paths. In your question, you only initialize one variable in each case of the switch, and neither in the default. You may know that they will all be initialized by the end of the loop, but the compiler isn't smart enough to figure that out.
There's also very few situations where you would want to do that anyway. If you find yourself needing to initialize different variables in different stages of a loop, there's a good chance you're doing something wrong or you could write your code more efficiently. It's hard to say how to simplify your actual code without seeing it, but for the example code you would get the same result by simply immediately initializing both variables.
If you must do it that way, and you know all of the variables will be initialized before they're used but the compiler doesn't, you can use implicitly unwrapped optionals:
var ex1: Example!
var ex2: Example!
// other code is the same
As with all uses of force/implicit unwrapping, this comes with the risk of your app crashing if you made a mistake or forgot an edge case, so I only recommend it if there aren't any other ways of doing it, or if the other ways require excessively complex code.
Related
I have the following class, grossly simplified
class MyClass {
var largeArray: [Int] = []
init() {
largeArray.reserveCapacity(10000000)
... lots of code to add 10000000 various elements to largeArray
}
func mutateArray(idx: Int) {
largeArray[idx] = someVal
}
}
Surprisingly, when profiling this code, calls to mutateArray turned out to be very expensive, with most of the time spent
in _ArrayBufferProtocol.init(copying:), and some in _swift_release_dealloc. The time spent is proportional to the number of calls to mutateArray, indicating
that this happens every time the method is called.
Why is this happening? Is there any way to avoid it?
Your array's buffer leaks out of its MyClass encapsulation somewhere.
If largeArray is initialized within a MyClass object, it has sufficient capacity reserved up front, and you've never let anyone else have access to your class, or alias it yourself, then you couldn't possibly cause a CoW copy.
You should set var largeArray to private. Not only will that enforce the capsulation you're in need of, but it'll also show you what else is accessing this.
I'm rewriting some code to append jobs to an array of closures rather than execute them directly:
var someObject: SomeType?
var jobsArray: [() -> ()] = []
// before rewriting
doExpensiveOperation(someObject!.property)
// 1st attempt at rewriting
jobsArray.append {
doExpensiveOperation(someObject!.property)
}
However, because the value of someObject might change before the closure is executed, I'm now adding a closure list as follows:
// 2nd attempt at rewriting
jobsArray.append { [someObject] in
doExpensiveOperation(someObject!.property)
}
Hopefully then if, say, someObject is subsequently set to nil before the closure executes, the closure will still access the intended instance.
But what's the neatest way to deal with the possibility that the value of .property might change before the closure is executed? Is there a better way than this?
// 3rd attempt at rewriting
let p = someObject!.property
jobsArray.append {
doExpensiveOperation(p)
}
I'm not very keen on this solution because it means changing the original line of code. I'd prefer this but it doesn't work:
// 4th attempt at rewriting
jobsArray.append { [someObject!.property] in
doExpensiveOperation(someObject!.property)
}
Pretty new to Swift, so all guidance gratefully received. Thanks!
A capture list such as [someObject] is actually syntactic sugar for [someObject = someObject], where the right hand side can be an arbitrary expression that gets bound to a new constant upon the closure being formed.
Therefore one option is to write your example as:
jobsArray.append { [property = someObject!.property] in
doExpensiveOperation(property)
}
In c++, one can introduce an alias reference as follows:
StructType & alias = lengthyExpresionThatEvaluatesToStuctType;
alias.anAttribute = value; // modify "anAttribute" on the original struct
Is there a similar syntactic sugar for manipulating a (value typed) struct in Swift?
Update 1: For example: Let say the struct is contained in a dictionary of kind [String:StructType], and that I like to modify several attributes in the the struct myDict["hello"]. I could make a temporary copy of that entry. Modify the copy, and then copy the temporary struct back to the dictionary, as follows:
var temp = myDict["hello"]!
temp.anAttribute = 1
temp.anotherAttribute = "hej"
myDict["hello"] = temp
However, if my function has several exit points I would have to write myDict["hello"] = temp before each exit point, and it would therefore be more convinient if I could just introduce and alias (reference) for myDict["hello"] , as follows:
var & alias = myDict["hello"]! // how to do this in swift ???
alias.anAttribute = 1
alias.anotherAttribute = "hej"
Update 2: Before down- or close- voting this question: Please look at Building Better Apps with Value Types in swift (from WWWDC15)!! Value type is an important feature of Swift! As you may know, Swift has borrowed several features from C++, and value types are maybe the most important feature of C++ (when C++ is compared to Java and such languages). When it comes to value types, C++ has some syntactic sugar, and my questions is: Does Swift have a similar sugar hidden in its language?. I am sure Swift will have, eventually... Please, do not close-vote this question if you do not understand it!
I have just read Deitel's book on Swift. While I'am not an expert (yet) I am not completely novel. I am trying to use Swift as efficient as possible!
Swift doesn't allow reference semantics to value types generally speaking, except when used as function parameters declared inout. You can pass a reference to the struct to a function that works on an inout version (I believe, citation needed, that this is implemented as a copy-write, not as a memory reference). You can also capture variables in nested functions for similar semantics. In both cases you can return early from the mutating function, while still guaranteeing appropriate assignment. Here is a sample playground that I ran in Xcode 6.3.2 and Xcode 7-beta1:
//: Playground - noun: a place where people can play
import Foundation
var str = "Hello, playground"
struct Foo {
var value: Int
}
var d = ["nine": Foo(value: 9), "ten": Foo(value: 10)]
func doStuff(key: String) {
let myNewValue = Int(arc4random())
func doMutation(inout temp: Foo) {
temp.value = myNewValue
}
if d[key] != nil {
doMutation(&d[key]!)
}
}
doStuff("nine")
d // d["nine"] has changed... unless you're really lucky
// alternate approach without using inout
func doStuff2(key: String) {
if var temp = d[key] {
func updateValues() {
temp.value = Int(arc4random())
}
updateValues()
d[key] = temp
}
}
doStuff2("ten")
d // d["ten"] has changed
You don't have to make the doMutation function nested in your outer function, I just did that to demonstrate the you can capture values like myNewValue from the surrounding function, which might make implementation easier. updateValues, however, must be nested because it captures temp.
Despite the fact that this works, based on your sample code, I think that using a class here (possibly a final class if you are concerned about performance) is really more idiomatic imperative-flavored Swift.
You can, if you really want to, get a raw pointer using the standard library function withUnsafeMutablePointer. You can probably also chuck the value into an inner class that only has a single member. There are also functional-flavored approaches that might mitigate the early-return issue.
I know why I would use a struct as opposed to a class, but how could I best tell which is being used by an API I'm using?
Obviously looking at the header file (or hopefully documentation) should make it immediately obvious. I am wondering if there is a way to know if the object I am using is a struct or class on face value though?
You can’t inherit from other structures or types. Classes have the ability to inherit functions, variables, and constants from parent classes.
In swift structs are value types while classes are reference types. Working with value types can make your code less error prone.
When you make a copy of a reference type variable, both variables are referring to the same object in memory. A change to one of the variables will change the other.
when you make a copy of a value type variable, the complete variable is copied to a new place in memory. A change to one of the copies will not change the other. If the copying of an object is cheap, it is far safer to make a copy than it is to share memory.
Auto completion in Xcode knows the type of type:
I'm not sure what you mean by "face-value". You can test to see if an object is an instance of a class by getting it's MirrorType using reflect and checking for the MirrorType's objectIdentifier property, like this:
struct TestStruct { }
class TestClass { }
let testStruct = TestStruct()
let testClass = TestClass()
if let x = reflect(testStruct).objectIdentifier {
println("I am a class...")
} else {
println("I am not a class...") // prints "I am not a class..."
}
if let x = reflect(testClass).objectIdentifier {
println("I am a class...") // prints "I am a class..."
} else {
println("I am not a class...")
}
This answer may be outdated with the upcoming release of Swift 1.2 (I do not have the new xCode beta so I can't say for sure), which I understand has better object introspection, but this does do the trick.
I'm writing an application Bluetooth-controlled keynote remote. This will be using AppleScript to control Keynote based on interactions with the CoreBluetooth framework.
Consider this class, which requires the use of an optional OSALanguage initializer.
class KeynoteController {
let applescriptLanguage: OSALanguage
init?() {
if let applescriptLanguage = OSALanguage(forName: "AppleScript") {
self.applescriptLanguage = applescriptLanguage
} else {
return nil // Compile error on this line
}
}
}
In this example, I want to fail initializing my KeynoteController if there's no OSALanguage named "AppleScript" (admittedly unlikely, but good design). However, I can't return from my initializer until all stored properties are populated.
I could make applescriptLanguage an optional, but since it's non optional and constant if initialization succeeds, this seems like a hack.
What's the correct way to design this?
The problem seems to come from trying to make applescriptLanguage a non-optional. The compiler wants it to be assigned a value, even if the object is failing initialization.
Fix it by making it optional property (implicitly unwrapped because it should never actually be nil after initialization).
let applescriptLanguage: OSALanguage!
This seems like a compiler error to me, but I have no idea what's going on under the hood.
Alternatively, you could temporarily assign it to a dummy value in the else block. This adds extra initialization time and memory allocation, so probably not the best idea, but it'll work if you really want to have the property be non-optional.
} else {
self.applescriptLanguage = OSALanguage()
return nil
}