Reputation: 17626
I need to create a "tools" class or struct with only static methods. As someone coming from a PHP background, structs don't really exist.
I was reading about this Why Choose Struct Over Class? but they do not talk about static structs or static classes.
Which should I use for static methods that are never instanced and why?
Example:
struct BasicTools {
static func split(str: String) -> [String]{
return str.characters.split{$0 == ","}.map(String.init)
}
}
vs
class BasicTools {
static func split(str: String) -> [String]{
return str.characters.split{$0 == ","}.map(String.init)
}
}
In use:
let StrArr: [String] = BasicTools.split("example,example2,example3")
Upvotes: 3
Views: 922
Reputation: 73196
As R Menke has already pointed out; since you have no instance of these utility classes/structure, it doesn't make much of a difference.
One possible vote for using classes over structs would be for the very specific situation where you declare your static
functions in a protocol which you specifically constrain to class
usage (protocol ...: class
): the counterpart for structures (protocol ...: struct
) is not available.
/* only class types can conform to this protocol */
protocol MyClassProtocol: class {
static func split(str: String) -> [String]
}
extension MyClassProtocol {
static func split(str: String) -> [String]{
return str.characters.split{$0 == "."}.map(String.init)
}
}
/* Use custom implementation */
class BasicTools: MyClassProtocol {
class func split(str: String) -> [String]{
return str.characters.split{$0 == ","}.map(String.init)
}
}
/* Use default implementation */
class OtherBasicTools: MyClassProtocol { }
BasicTools.split("foo,bar") // ["foo", "bar"]
OtherBasicTools.split("foo.bar") // ["foo", "bar"]
In your context (class/struct of only utility tools), the above :class
constraint for MyClassProtocol
is not really relevant. It is, however, relevant if the MyClassProtocol
protocol---in addition to containing static tools---were to be used as a delegate in a context were a strong reference to this delegate would create a retain cycle. In such a case, we need to constrain the delegate protocol to only be available (for conformance) to reference types, which allow us to use weak references to the delegate itself. In this case, naturally our toolbox must be use in class
context (and not in a structure).
E.g., consider the following example, where we have some static toolbox of functions we want to make available to all our different kind of delegates:
/* Delegate toolbox: static/class functions for use
with your delegates */
protocol MyBasicDelegateTools {
static func delegateCalled() -> ()
}
extension MyBasicDelegateTools {
static func delegateCalled() -> () {
print("Logging: delegate was called")
}
}
Used here by some delegate:
/* Delegate with access to your basic delegate tools */
protocol MyDelegate: class, MyBasicDelegateTools {
func arrUpdated(baz: [Int])
}
/* It's inherrent here that we let the reference to
the delegate be weak, to avoid a retain cycle. */
class Foo {
private var baz : [Int] = [] {
didSet {
if let _ = delegate?.arrUpdated(baz) {
delegate?.dynamicType.delegateCalled()
}
}
}
weak var delegate: MyDelegate?
}
/* Why? Since 'foo' in 'Bar' keeps a strong reference to the 'Foo' instance */
class Bar: MyDelegate {
var foo : Foo
var idx: Int = 0 {
didSet {
print("idx updated: \(idx)")
}
}
init(foo: Foo) {
self.foo = foo
self.foo.delegate = self
}
// MyDelegate
func arrUpdated(baz: [Int]) {
idx = baz.count
}
}
Example usage:
var a = Foo()
var b = Bar(foo: a)
a.baz.append(1)
// idx updated: 1 <-- from custom delegate
// Logging: delegate was called <-- from delegate toolbox
Upvotes: 1
Reputation: 8391
If you only use static functions there is no difference at all.
More structure can be accomplished with struct
and typealias
. (also true for class
but no pun there)
struct MathUtils {
static func someMaths(withInt int:Int) -> Int {
return int
}
}
struct StringUtils {
static func someEditing(withString string:String) -> String {
return string
}
}
struct Utils {
typealias Maths = MathUtils
typealias Strings = StringUtils
}
Utils.Maths.someMaths(withInt: 10)
Upvotes: 3