7
\$\begingroup\$

I'm porting some Obj-C code to Swift, and I've written the following code to allow me to deal with "static local variables" which do not exist in Swift.

A static local variable has these requirements:

  • it is shared between all instances that access it
  • it has one assignment method which only sets the value when it is first declared.
  • has another assignment method which sets its value normally (i.e. any time it is used)

There has to be a better way then what I have coded. For starters, I know that using UnsafeBitcast is not good practice.

class Container<T:Any>{

    var _memory:Any
    var memory:T {
        get {

            if let typed_value = self._memory as? T {
                // for value types, such as "String"
                return typed_value
            } else {
                // for types conforming to "AnyObject", such as "NSString"
                return unsafeBitCast( self._memory, T.self )
            }

        }
        set {
            self._memory = newValue
        }
    }

    init( memory:Any ){
        self._memory = memory
    }

}

var Static_Containers = [String:AnyObject]()

func static_var <T:Any>(
    value:T,
    file: StaticString = __FILE__,
    line: UWord = __LINE__,
    col: UWord = __COLUMN__,
    fun: StaticString = __FUNCTION__
) -> Container<T> {

        let unique_key = "FUNC_\(fun)__LINE\(line)_COL\(col)__FILE_\(file)"

        let needs_init = !contains( Static_Containers.keys, unique_key )

        if needs_init {
            Static_Containers[unique_key] = Container<T>( memory:value )
        }

        return Static_Containers[unique_key]! as! Container<T>

}

Here's a couple tests:

func test_with_nsstring( str:NSString, init_only:Bool ) -> NSString {   
    var stat_str = static_var( str )
    if !init_only {
        stat_str.memory = str
    }
    return stat_str.memory
}
test_with_nsstring( "this should get set", true )
test_with_nsstring( "this should be ignored", true ) // only repeated declaration
test_with_nsstring( "this should change the value", false )
test_with_nsstring( "as should this", false )

func test_with_int( i:Int, init_only:Bool ) -> Int {
    var stat_int = static_var( i )
    if !init_only {
        stat_int.memory = i
    }
    return stat_int.memory
}
test_with_int( 0, true ) 
test_with_int( 1, true ) // only repeated declaration
test_with_int( 2, false ) 
test_with_int( 3, false ) 

func test_with_optstr( optstr:String?, init_only:Bool ) -> String? {
    var stat_optstr = static_var( optstr )
    if !init_only {
        stat_optstr.memory = optstr
    }
    return stat_optstr.memory
}
test_with_optstr( nil, true ) 
test_with_optstr( "this should be ignored", true ) // only repeated declaration
test_with_optstr( "this should change the value", false )
test_with_optstr( "as should this", false )

When I test this code in a Playground, it seems to behave correctly. I'd just like a less nutty, and less brittle, way to accomplish this.

\$\endgroup\$
  • 1
    \$\begingroup\$ Have you seen this? stackoverflow.com/questions/25354882/… \$\endgroup\$ – nhgrif Mar 6 '15 at 19:14
  • \$\begingroup\$ @nhgrif It works. Can you please repost your comment as an answer, so I can give you proper credit? Thanks! \$\endgroup\$ – Charlesism Mar 6 '15 at 19:53
8
\$\begingroup\$

The only work around for this that I've come up with thus far is something that looks like this:

func foo() -> Int {
    struct Holder {
        static var timesCalled = 0
    }
    return ++Holder.timesCalled;
}

It's pretty clunky in my opinion, and I'm not sure why Apple doesn't just allow straight static function variables, but this seems quite a bit cleaner than your approach.

\$\endgroup\$
  • \$\begingroup\$ I imagine Apple will get around to it. In the meantime, your solution should work fine. Thanks! \$\endgroup\$ – Charlesism Mar 6 '15 at 22:42
-2
\$\begingroup\$

it seems working in Xcode 6.3.1.. as stated in Apple manuals, we can use static as we do in "C". (let's forget theoretical differences... )

import Foundation

class Shape{

    private static var shapeCount = 0

    init() {
        Shape.shapeCount++
        println("I am \(Shape.shapeCount)' shape")
    }


    deinit{
        Shape.shapeCount--
    }


    class func ClassInfo()->String{
        return "we have \(Shape.shapeCount) instances"
    }
}



class Circle : Shape {

    override class func ClassInfo()->String{
        return "we have \(Circle.shapeCount) instances"
    }

}


var s1 : Shape? = Shape()
var s2 : Shape? = Shape()
var c1 : Shape? = Circle()

println(Shape.ClassInfo())
println(Circle.ClassInfo())


s1 = nil
s1 = nil
c1 = nil

println(Shape.ClassInfo())

As You can see, shapeCount seems working as expected, even if we cannot use "class" qualifier.

OR you can use exactly as in old "C" program, moving property outside ALL the class doing:

import Foundation

private  var shapeCount = 0



class Shape{

....

(God bless us.. but the effect is the same)

Another big issue to be noted is thread-safe access..

\$\endgroup\$
  • 3
    \$\begingroup\$ Sorry, but this isn't the same. This is an class variable (versus an instance variable). It doesn't give us static variables scoped to the function/method (like we can do in C/ObjC). \$\endgroup\$ – nhgrif May 13 '15 at 10:41
  • \$\begingroup\$ I agree is NOT the same, but form a PURE porting point of view from OBJC both works: a) private var shapeCount = 0 b) class .... private static var shapeCount = 0 conceptually a big difference but may better mode a) if you need to mimic objC instead approach with struct is ugly, as a) is present since betas of swift. \$\endgroup\$ – ingconti Aug 8 '15 at 9:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.