Is it possible to add subclassed objects to a static array inside the parent class? I would like to run a function on all instances created. Another SO question describes being able to loop over an array to call a function on each instance and I think this is the end result I would like as well but my problem is even getting the instances into a static array that keeps track of all instances.
Of course my code is more modular but here is a simplified view of the code and hierarchy:
class Jungle {
static var jungle: [Animals] = []
}
class Tigers: Animals {
static var tigerPopulation: Int = 0
override init(name:String){
super.init(name: name)
Tigers.tigerPopulation += 1
}
deinit {
Tigers.tigerPopulation -= 1
}
}
class Monkeys: Animals {
static var monkeysPopulation: Int = 0
override init(name: String){
super.init(name: name)
Monkeys.monkeysPopulation += 1
}
deinit {
Monkeys.monkeysPopulation -= 1
}
}
class Snakes: Animals {
static var snakePopulation: Int = 0
override init(name: String){
super.init(name: name)
Snakes.snakePopulation += 1
}
deinit {
Snakes.snakePopulation -= 1
}
}
I get the feeling that I should have created the Jungle class first so they all would subclass from Jungle I guess but I'm still stumped on how I would get the instances into an array.
Instead of trying to trigger some behavior to increase the monkey count, tiger count etc from within an initializer, it will be easer to just add everything to an animals array, and then use the animals array to figure out how many Tigers/Snakes/Monkeys there are.
Basing the count off that single array is going to be easier in the long term than having a bunch of different variables, and making sure they're in sync (e.g. by decreasing during deinit).
Here's a minor reshuffle of your jungle, animal, and animal subclasses:
class Jungle {
var animals: [Animal] = []
func addAnimal(animal: Animal) {
animals.append(animal)
}
// this is a computed property, everytime you access it
// via jungle.tigerPopulation, the code inside this statement
// executes, and you get the value that is returned
var tigerPopulation: Int {
// tigers is a new array containing only tigers
let tigers = animals.filter{ animal in
return animal is Tiger
}
// return the number of items in the tigers array
return tigers.count
}
}
class Animal {
let name: String
init(name: String) {
self.name = name
}
func sayHello() {
print("I'm an animal")
}
}
class Tiger: Animal {
// tiger specific stuff
override func sayHello() {
print("I'm a tiger called", name)
}
}
class Monkey: Animal {
// monkey specific stuff
}
class Snake: Animal {
// snake specific stuff
override func sayHello() {
print("I'm a snake called", name)
}
}
Something to point out is that because each of the animal subclasses are currently doing the same thing (assigning the name) you don't need to override the initializer in the subclasses - that's the value of class inheritance!
You can override the subclasses sometimes in the future if an animal has to do something that's different from other animals.
The most interesting thing that's happening here is the computed property that calculates the tiger population by filtering the tigers in the animals array, and returning the count, this way you're not manually +/- 1 the population count, but just calculating it whenever you ask for it.
Then you'd use your jungle object something like this:
let jungle = Jungle()
// jungle.animals -> []
// jungle.animals.count -> 0
// jungle.tigerPopulation -> 0
jungle.addAnimal(animal: Tiger(name: "Tony"))
// jungle.animals -> [Tiger()]
// jungle.animals.count -> 1
// jungle.tigerPopulation -> 1
jungle.addAnimal(animal: Snake(name: "Susan"))
// jungle.animals -> [Tiger(), Snake()]
// jungle.animals.count -> 2
// jungle.tigerPopulation -> 1
for animal in jungle.animals {
animal.sayHello()
}
// prints ->
// I'm a tiger called Tony
// I'm a snake called Susan
Related
I have a protocol, structure and instances + protocol and class, which should change the instances of the structure:
protocol Cars {
var car: String { get set }
var accesories: [String] { get set }
}
struct Car: Cars {
var car: String
var accesories: [String]
}
var carOne = Car(car: "Car One", accesories: ["accessoryOne", "accessoryTwo"])
var carTwo = Car(car: "Car Two", accesories: ["accessoryTwo"])
protocol Dealership {
static var cars: [Car] { get set }
static func addAccesories(add: [String])
}
Next, in DealershipOne I want to make a func addAccesories that will add an array of strings to the instance property, I try do it this way and some other ways, but I get the error Cannot use mutating member on immutable value: '$0' is a 'let' constant.
class DealershipOne: Dealership {
static var cars = [carOne, carTwo]
static func addAccesories(add: [String]) {
cars.forEach{ $0.accesories.append(contentsOf: add) } // ERROR
}
}
How do I resolve this?
You can not add new value to direct iterated objects. $0 is a let object so you can not add value.
A solution to use index and update the value.
cars.indices.forEach{ cars[$0].accesories.append(contentsOf: add) }```
The anonymous closure parameter ($0) within your forEach loop is an immutable copy of a car from the cars you’re trying to iterate.
You could make a mutable copy with var car = $0, but even that won’t help, because you would be mutating the copy, and not the actual cars in the static cars array.
If you would like to keep using struct to model your cars, then the only way to modify properties of those cars within the array is to get an l-value (in c++ lingo) via assignment through a subscript:
func add(accessories: [String]) {
for i in cars.insides {
cars[i].accessories += accessories
}
}
(I also made some minor tweaks to make this more idiomatic, such as t changing the function name, and using the += operator for arrays instead of Array.append(contentsOf:))
Say for example I have an array that needs to be used on multiple classes, if I need a worker to update the values of this array how do I make it so that when I add new values to the said array the object worker's value also changes.
Example:
class Object {
var id: Int
var foo: String
var bar: Int
init(id: Int, foo: String, bar: Int) {
self.id = id
self.foo = foo
self.bar = bar
}
}
class ObjectWorker {
var objects: [Object]
init(objects: [Object]) {
self.objects = objects
}
func updateObjects(withId id: Int) {
self.objects.forEach { $0.foo = "a different value" }
}
}
class SomeClass {
// this declaration will happen on more than one class
var objects: [Object] = ... // let's just say there are 10 objects here
lazy var worker = ObjectWorker(objects: self.objects)
init() {
// to initialize the workers
_ = worker
print(objects.count) // 10
print(worker.objects.count) // 10
let newObjects: [Object] = ... // let's say this has 5 new values
objects.append(contentsOf: newObjects)
print(objects.count) // 15
print(worker.objects.count) // 10
}
}
I have tried making the ObjectWorker's init be an inout parameter like this init(objects: inout [Object] but even then the result is still the same. The updateObjects works though even if the init is not an inout parameter.
Note:
I know I can do this by using the ObjectWorker as the container of the objects instead of what is currently going on in here, but is there a way to do this without doing that?
I can also use static functions instead, but let's not go there
As already mentioned, Swift Arrays are value types so ObjectWorker gets a copy of the array. If you don't want to use ObjectWorker as a container, you could use an NSArray instead (which is a reference type).
Say you have
class Blah {
}
and then I'm going to have [Blah]
However, the Blah array is going to work a bit differently from a normal array.
For example, I want count to work like this, say
override count {
c = super.count // an ordinary count of the array
y = count of items where blah.color = yellow
return y
}
Of course, I know how to override count (or whatever) by subclassing the appropriate array concept in Swift.
But how can I override count "only in" the array [Blah] ... is that possible?
Use case - maybe there's a better way - Blah has a number of concrete subtypes A:Blah, B:Blah .. F:Blah I want to filter [Blah] so it only returns certain of them (say, "B and D types only") when you enumerate, and the count, etc, would only be for the turned-on subtypes. I appreciate Swift's slice and so on could be relevant here.
Like people are commenting, you don't really want to override count. Here's some code that shows why that's not going to work and gives another possible solution.
//: Playground - noun: a place where people can play
class Blah {
let include: Bool
init(include: Bool) {
self.include = include
}
}
// This code "works", but you'll get an error that "count" is ambiguous because
// it's defined in two places, and there's no way to specify which one you want
//extension Array where Element: Blah {
//
// var count: Int {
// return reduce(0) { result, element in
// guard element.include else {
// return result
// }
//
// return result + 1
// }
// }
//
//}
// But you could add a helper to any blah sequence that will filter the count for you
extension Sequence where Iterator.Element: Blah {
var includedBlahCount: Int {
return reduce(0) { result, blah in
guard blah.include else {
return result
}
return result + 1
}
}
}
let blahs = [Blah(include: false), Blah(include: true)]
print(blahs.count) // 2
print(blahs.includedBlahCount) // 1
So i'm kinda stuck on somethng.
i kinda want a random drop wen i kill my monster.
this is what i got.
i've got a weapon1
class weapon {
var str = Int
}
class Weapon1: weapon {
override init() {
super.init()
str = 10
}
class Weapon2: weapon {
override init() {
super.init()
str = 20
}
i've got a monster
class monster {
str = 20
def = 10
drops = [weapon1(), Weapon2()]
}
and i have a place to link my drops to if my monster is death
var itemdrops = weapon()
and when my monster dies it should link my monster drop to my itemdrops..
so i can use it somewhere like in an inventory.
but when i try to do it like this
random = (arc4random_uniform(UInt32(monster.items.count-1)))
//-1 becous it would say 2 items and an array starts at 0
itemdrops = monster.items[random]
but then items is in white..
The problem is that items is an instance property of monster:
class monster {
str = 20
def = 10
drops = [weapon1(), Weapon2()]
}
But when you try to access it, you try to access it through the class:
monster.items[random]
The class monster has no class/static property items, so your code cannot compile.
This is a good reason for using capital letters for class names, as you are supposed to. It helps you keep track of whether this thing is a class or an instance. Example:
class Monster {
str = 20
def = 10
drops = [Weapon1(), Weapon2()] // assume you've named these classes correctly
}
Now, in your code, you would say:
let aMonster = Monster() // an _instance_
var itemdrops = aMonster.items[random]
I am trying to create an array that will store my class objects. The createEnemies method is called when a level is started. Which should then create the enemy objects. However I don't understand how to do that. It should be created after "if(levelNumber < 5)"
class level {
class func createEnemies() {
numEnemies = Int(floor(levelNumber * 1.5 + 10))
println("Number of Enemies this level: \(numEnemies)")
if(levelNumber < 5){
//Create numEnemies amount of class objects
}
}
}
//Enemy Variables
var enemiesKilled = 0
class enemy {
class func enemiesKilled() {
}
class standard {
var health:Int = 10
var name:String = "Standard"
var worth:Int = 10
var power:Int = 10
init () {
}
func kill() {
}
func damage(damage: Int) {
self.health -= damage
println("\(self.name) was damaged \(damage)")
if(self.health <= 0){
self.kill()
}
}
}
For Swift 3.1
var enemies:[enemy] = [enemy]()
Create an array of elements of a custom class like this:
var enemies = [enemy]()
You can add elements to it like this:
enemies.append(anEnemy: enemy)
If you want to have a specific number of enemies in the array there are several ways to achieve this (I write Enemy instead of enemy because the first letter of a class name is usually uppercase):
// "old fashioned" for loop
var enemies = [Enemy]()
for _ in 1...numEnemies {
// call initializer of Enemy
enemies.append(Enemy())
}
// my personal preference (Range has a method named map which does the same as Array)
// without the "_" you could also access the numbers if you want
let enemies = (1...numElements).map{ _ in Enemy() }
If you need to access the array later on you should declare the variable under your comment //Enemy Variables.