I have a class Population
which upon initialisation gets defined a language
. For the sake of this example, every member of that population only speaks this particular language. I then have a Member
class defined within the Population
for the population members. In this class, I would like to store the language as a static type property to be able to reference if from Member
methods like in the example code below.
This seems more efficient to me than (a) always have to pass the language
as a parameter when I call a method on a members
element or (b) to store the language on each Member
instance. However, below code does not quite seem to work and I wonder how to best implement this. Any ideas / best-practices? Apologies if this may sound rather trivial or in case I am completely on the wrong path here.
class Population {
var language: String
var members: [Member]
init(language: String) {
self.language = language
for _ in 1...10 {members.append(Member())}
}
class Member {
static let language: String
func speak() {
switch self.language {
case "english": print("speak some english")
}
}
func write() {
switch self.language {
case "english": print("write some english")
}
}
}
}
CodePudding user response:
I think there is some confusion about what static
means, and it probably stems from class Member
being nested in class Population
. All nesting types do is enclose the nested type in a namespace, which helps a) avoid name collision on types in global scope, and b) enables you to hide it as an implementation detail of the type in which it is nested, if you choose to make it a private
type.
A nested type does not belong to an instance of the enclosing type. So if you define language
as a static
property of Member
, then it has one value for class Member
, regardless of how many instances of Population
you have with different languages.
What you want is for each instance of Member
to have the language
of the Population
to which it belongs. You can accomplish that a couple of different ways, but they all involve storing something in Member
.
Option 1. Make language
an instance property of Member
, and set it in Member.init(language: String)
.
Option 2. Store a reference to the Population
to which the Member
belongs (I'll assume it's let population: Population
), and make language
a computed property which returns population.language
.
Option 2 is more flexible, because you can leverage having the population to fetch other Member
values that it gets from its Population
, but it requires some careful consideration. Does it make sense in your app for a Member
to exist outside of a Population
? If yes, then you probably want to define it like weak var population: Population?
. If no, a Member
cannot exist outside of a Population
, then unowned let population: Population
could be preferred (though the weak
version could be used too). The point here is to avoid reference cycles that would cause a memory leak.
So maybe something like this:
class Population {
var language: String
var members: [Member] = []
init(language: String) {
self.language = language
for _ in 1...10 {members.append(Member(population: self))}
}
class Member {
unowned let population: Population
var language: String { population.language }
init(population: Population) { self.population = population }
func speak() {
switch self.language {
case "english": print("speak some English")
case "german" : print("spreche ein Bißchen deutsch")
// other cases go here
default: print("mmmm mmmm mmm mmm") // I'm a mute
}
}
func write() {
switch self.language {
case "english": print("write some English")
case "german" : print("schreibe ein Bißchen deutsch")
// other cases go here
default: print("h8hta;lushgaih9732 8@#Y"); // Can't write
}
}
}
}
Though off-topic, I would recommend making language an enum
, Then you can remove the default
cases from the switch
statements, and the compiler will helpfully, though not tactfully, show you all of the switch
statements that need to be updated when you add a new language. If you like you can even make the enum
based on String
so you can still access the String
value wherever that might be useful:
enum Language: String
{
case arabic = "arabic"
case chinese = "chinese"
case english = "english"
case french = "french"
case german = "german"
case japanese = "japanese"
case portuguese = "portuguese"
case spanish = "spanish"
// etc...
}
Then if you want the string value you can refer to language.rawValue