Strange Behavior with Swift Compiler

Strange behavior of Swift

The '!' indicates forced unwrapping of an optional. However it's type is still optional and therefore can be nil.

(You're just telling the compiler that you guarantee it won't become nil.)

A line like...

let v: Int = myVar

...at the end of your code will get you the crash you expect.


Another example is the way outlets are handled. They are typically defined such as:

@IBOutlet weak var detailDescriptionLabel: UILabel!

Before viewDidLoad is called, it is nil. Again, the '!' is just a promise to the compiler that it won't be used until it contains a valid reference.

Swift / Xcode strange behaviour: Use of undeclared type error shown in case of struct used before declaration

In screen 1 settingsDb variable is declared in global namespace, in this case order of declaration matters, so all types of top-level declarations must be declared before usage.

If one needs more details, look for "swift top-level", official developer.apple.com blog on this is here

Strange Behaviour in Swift: constant defined with LET but behaving like a variable defined with VAR

This looks like a compiler optimization bug. In the swift language guide it says:

Behind the scenes, Swift’s compiler optimizes string usage so that
actual copying takes place only when absolutely necessary. This means
you always get great performance when working with strings as value
types.

I dug around a little and found that it's possible to get the memory address of a string via str._core._baseAddress (Note: that's probably supposed to be a private var in swift's core lib, but it seems to have been overlooked).

If I modify the original code to print the address of str and match0/match1, this is what I get:

+    str address: 0x0000000100006130; match0 address: 0x0000000100006130
+ str address: 0x0000000100511f70; match0 address: 0x0000000100006130
+ str address: 0x0000000100511f70; match0 address: 0x0000000100006130
+ str address: 0x0000000100511f70; match1 address: 0x0000000100511f70
z str address: 0x0000000100511f70; match1 address: 0x0000000100511f70

Notice how the first one prints the same address since no copy needed to be made yet but, once the first str.removeRange(range0) is called, the addresses change because it copies str to a new location.

That last one is the problem. From what I can tell, the compiler should have copied str to a new memory location when str.removeRange(range1) was called, but it didn't (bug). Since it didn't, str and match1 still have the same base address so match1 ends up printing "z".

Sounds like it might be bug report time.


Update: Workaround

It looks like an easy workaround to this is to force the compiler to trigger a string copy by calling stringByAppendingString("") before the second rangeOfString call:

var str = "+y+z*1.0*sum(A1:A3)"

if let range0 = str.rangeOfString("^\\+|^\\-|^\\*|^\\/", options: NSStringCompareOptions.RegularExpressionSearch){
// ...
}

// Force a string copy by appending an empty string to get around optimization bug
str = str.stringByAppendingString("")

if let range1 = str.rangeOfString("^\\+|^\\-|^\\*|^\\/", options: NSStringCompareOptions.RegularExpressionSearch){
// ...
}

Data ranged subscribe strange behavior

d[5..<8] returns Data.Slice – which happens to be Data. Generally, slices share the indices with their base collection, as documented in Slice.

One possible reason for this design decision is that it guarantees that subscripting a slice is a O(1) operation (adding an offset for accessing the base collection is not necessarily O(1), e.g. not for strings.)

It is also convenient, as in this example to locate the text after the second occurrence of a character in a string:

let string = "abcdefgabcdefg"

// Find first occurrence of "d":
if let r1 = string.range(of: "d") {
// Find second occurrence of "d":
if let r2 = string[r1.upperBound...].range(of: "d") {
print(string[r2.upperBound...]) // efg
}
}

As a consequence, you must never assume that the indices of a collection are zero-based (unless documented, as for Array.startIndex). Use startIndex to get the first index, or first to get the first element.



Related Topics



Leave a reply



Submit