I want to define a method that can destroy the instance it belongs to when a variable in this class has increased to a certain value. I attempted to do something like following:
var calledTimes = 0 //some other method would update this value
func shouldDestroySelf(){
if calledTimes == MAX_TIMES {
denit
}
}
But i would get error message saying "Expect '{' for deinitializers".
Is there anyway to self-destruct within the class?
You can not call deinit method. From Apple Docs: Deinitializers are called automatically, just before instance deallocation takes place. You are not allowed to call a deinitializer yourself.
You should set that instance to nil in order to destroy that instance provided that all references to that instance are broken .
You can create a protocol which does the self destruction based on a certain criteria. Here's an example using a class
class SelfDestructorClass
{
var calledTimes = 0
let MAX_TIMES=5
static var instancesOfSelf = [SelfDestructorClass]()
init()
{
SelfDestructorClass.instancesOfSelf.append(self)
}
class func destroySelf(object:SelfDestructorClass)
{
instancesOfSelf = instancesOfSelf.filter {
$0 !== object
}
}
deinit {
print("Destroying instance of SelfDestructorClass")
}
func call() {
calledTimes += 1
print("called \(calledTimes)")
if calledTimes > MAX_TIMES {
SelfDestructorClass.destroySelf(self)
}
}
}
You can derive your class from this class and then call call() on those object. The basic idea is to have the ownership of the object at one and only one place only and then detach the ownership when the criteria is met. The ownership in this case is a static array and detaching is removing it from the array. One important thing to note is that you have to use weak reference to the object wherever you are using it.
E.g.
class ViewController: UIViewController {
weak var selfDestructingObject = SelfDestructorClass()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func countDown(sender:AnyObject?)
{
if selfDestructingObject != nil {
selfDestructingObject!.call()
} else {
print("object no longer exists")
}
}
}
Related
Consider the following code:
class ViewController: UIViewController {
var dummy = DummyClass()
override func viewDidLoad() {
super.viewDidLoad()
var copyDummy = dummy
if (copyDummy === dummy){
print("Same instance")
}
increment(©Dummy)
}
func increment(_ number: inout DummyClass) {
number.change += dummy.change
}
}
class DummyClass {
var change = 10
}
Now from my understanding, both copyDummy and dummy point to the same memory location.
When I call func increment using increment(&dummy) there is a runtime error since I am trying to write and read the same memory location simultaneously.
But when I callincrement(©Dummy) there is no error.
Why such behaviour if copyDummy is also accessing the same memory location as dummy variable?
Sorry if this turns to be a very basic question and/or if I have got my understanding incorrect. I am trying to wrap my head around this from quite some time.
Thanks in advance!
Modified code as per helpful suggestions:
class ViewController: UIViewController {
var dummy = DummyClass()
override func viewDidLoad() {
super.viewDidLoad()
increment(dummy)
}
func increment(_ number: DummyClass) {
number.change += dummy.change
}
}
class DummyClass {
var change = 10
}
Isnt func increment writing the same memory location ie. numbers.change from where its also reading ie. dummy.change
I'm fairly certain there is something obvious I am missing and/or doing incorrectly here.
I have a base class with a handleTapped() function that handles a button being tapped on.
In a subclass, I want to override that function but need to access the variables that I declare in the handleTapped() function in the parent class.
Ex:
class ParentClass {
#objc func handleTapped(sender: UITapGestureRecognizer) {
var a = [Int]()
var b = 10
if a[1] == b {
// do something
}
}
}
class SubClass: ParentClass {
#objc override func handleTapped(sender: UITapGestureRecognizer) {
super.handleTapped(sender: sender)
if a[1] == b {
// do something else
}
}
}
Variables declared in a function cannot be accessed from outside of that scope, including by methods that override their behaviour (unless you use them as a return of that function, which we can't do here).
In this case, you'll need to store the necessary information in the parent class itself, and make sure the access rights are set correctly (internal, or public depending on how your project is set up) so that the Subclass can access and modify that data.
Alternatively, there may be an alternative way to implement this. This might be a candidate where duplication of the code is a more preferable option to exposing data to all subclasses.
Take the variables out of the method and into the class like so:
class ParentClass {
var a = [Int]()
var b = 10
func handleTapped(sender: UITapGestureRecognizer) {
if a[1] == b {
// do something
}
}
}
class SubClass: ParentClass {
override func handleTapped(sender: UITapGestureRecognizer) {
super.handleTapped(sender: sender)
if a[1] == b {
// do something else
}
}
}
I have the following code in Playground -I'm learning delegation-...
import UIKit
protocol FollowThisProtocol {
func passingTheValue(aValue: String)
}
class IPassTheValues{
var aDelegate: FollowThisProtocol!
func runThisFunc(){
aDelegate.passingTheValue(aValue: "I like this game")
}
}
class IReceiveTheValues: FollowThisProtocol{
var localString: String!
var instanceOfClass: IPassTheValues!
func runReceivefunc(){
instanceOfClass.aDelegate = self
}
func passingTheValue(aValue: String) {
localString = aValue
}
}
When I attempt to
print(IReceiveTheValues().localString)
it's giving me nil
It also gives me nil if I run the following lines before attempting to print(IReceiveTheValues().localString)...
IPassTheValues()
IReceiveTheValues()
could you please help me understand why the value is not being passed from the 1st class to the 2nd..?
Or if you can spot something in my code that is contradicting itself, could you please point it out..?
Appreciate your time and help.
You need to create the IPassTheValues object before assigning yourself as the delegate, and then call runThisFunc() on the instance:
func runReceivefunc(){
instanceOfClass = IPassTheValues()
instanceOfClass.aDelegate = self
instanceOfClass.runThisFunc()
}
Then test:
// Create the `IReceiveTheValues` object
let irtv = IReceiveTheValues()
// Run the method
irtv.runReceivefunc()
// Get the resulting string
print(irtv.localString)
I suggest 2 other changes. Make your delegate weak so that you don't get a retain cycle which makes it impossible to delete either object. In order to do that, you will need to add : class to your protocol declaration because only reference objects (instances of a class) can be weak.
Here's the modified code. Try it and see what happens when you delete weak.
protocol FollowThisProtocol: class {
func passingTheValue(aValue: String)
}
class IPassTheValues{
weak var aDelegate: FollowThisProtocol!
func runThisFunc(){
print("Calling delegate...")
aDelegate.passingTheValue(aValue: "I like this game")
}
deinit {
print("IPassTheValues deinitialized")
}
}
class IReceiveTheValues: FollowThisProtocol{
var localString: String!
var instanceOfClass: IPassTheValues!
func runReceivefunc(){
instanceOfClass = IPassTheValues()
instanceOfClass.aDelegate = self
instanceOfClass.runThisFunc()
}
func passingTheValue(aValue: String) {
print("Receiving value from helper object...")
localString = aValue
}
deinit {
print("IReceiveTheValues deinitialized")
}
}
func test() {
let irtv = IReceiveTheValues()
irtv.runReceivefunc()
print(irtv.localString)
}
test()
I want to ensure by compiler that CarViewController only receives a Car in the vehicle property.
Given the following swift example code:
class Vehicle {
func doSomething(){}
}
class Car: Vehicle {
func doCarThings(){}
}
class VehicleViewController : UIViewController {
var vehicle : Vehicle!;
override func viewDidLoad() {
super.viewDidLoad();
vehicle.doSomething();
}
}
class CarViewController:VehicleViewController {
var vehicle: Car!
override func viewDidLoad() {
super.viewDidLoad();
vehicle.doCarThings();
}
}
I get the following error: Cannot override mutable property 'vehicle' of type 'Vehicle!' with covariant type 'Car!'
I tried with a generics-based approach:
class Vehicle {
func doSomething(){}
}
class Car: Vehicle {
func doCarThings(){}
}
class VehicleViewController<T:Vehicle> : UIViewController {
var vehicle : T!;
override func viewDidLoad() {
super.viewDidLoad();
vehicle.doSomething();
}
}
class CarViewController:VehicleViewController<Car> {
override func viewDidLoad() {
super.viewDidLoad();
vehicle.doCarThings();
}
}
It is correct but using generics in storyboard classes results in errors (since they get compiled to objective-c).
How can I do this without using generics?
Thanks!
I'm really not sure about the design here, but to accomplish what you want you could do:
class CarViewController: VehicleViewController {
var vehicleAsCar: Car { return self.vehicle as! Car }
override func viewDidLoad() {
super.viewDidLoad();
vehicleAsCar.doCarThings();
}
}
But this seems very smelly. Safer night be:
class CarViewController: VehicleViewController {
override var vehicle: Vehicle! {
didSet {
assert(vehicle is Car, "Attempt to set vehicle to non-Car")
}
}
var vehicleAsCar: Car { return self.vehicle as! Car }
override func viewDidLoad() {
super.viewDidLoad();
vehicleAsCar.doCarThings();
}
}
Taken from here:
Overriding Property Getters and Setters
You can provide a custom getter (and setter, if appropriate) to
override any inherited property, regardless of whether the inherited
property is implemented as a stored or computed property at source.
The stored or computed nature of an inherited property is not known by
a subclass—it only knows that the inherited property has a certain
name and type. You must always state both the name and the type of the
property you are overriding, to enable the compiler to check that your
override matches a superclass property with the same name and type.
Seems like you cant do that.
This question already has an answer here:
How to call deinit in Swift [duplicate]
(1 answer)
Closed 5 years ago.
I came across something that's peculiar and interesting and would love to get inputs from anyone. So to start off with lets take this definition of the class:
class TestClass:NSObject {
var s1 = NSHashTable<TestClass>(options: .weakMemory)
func doit(bla:TestClass) {
s1.add(bla)
bla.s1.add(self)
}
deinit {
print("Deinit")
}
}
Now lets consider the following:
var t1:TestClass? = TestClass()
var t2:TestClass? = TestClass()
If we did the following deinit gets called:
t1?.s1.add(t2!)
t2?.s1.add(t1!)
t1 = nil // This will result in deinit being called
Now lets do the same thing but by calling doit() method
t1?.doit(bla:t2!)
t1 = nil // Deinit doesn't get called for some reason
The question here is why isn't deinit being called in this situation? What is so different about this since it essentially has the same reference assignment as the first method?
I would love to get input from anyone on this.
As usual, the problem is that you are trying to test this in a playground. Don't. Playgrounds are the work of the devil.
Test in an actual app project and you will see that deinit is called.
Example (iOS, but the equivalent in macOS would do fine):
import UIKit
class TestClass:NSObject {
var s1 = NSHashTable<TestClass>(options: .weakMemory)
func doit(bla:TestClass) {
s1.add(bla)
bla.s1.add(self)
}
deinit {
print("Deinit")
}
}
class ViewController: UIViewController {
var t1:TestClass? = TestClass()
var t2:TestClass? = TestClass()
override func viewDidLoad() {
super.viewDidLoad()
t1?.doit(bla:t2!)
t1 = nil // --> "Deinit"
print(t2?.s1) // --> it's empty
}
}
deinit is not called because you've created reference cycle.
First you're creating sctrong reference from self to bla: s1.add(bla)
Second you create strong reference from bla to self: bla.s1.add(self)
And now they both have references to each other, so they won't deinit if you just nullify one of them.
I've modified your TestClass to remove reference cycle:
class TestClass:NSObject {
weak var s1 = NSHashTable<TestClass>(options: .weakMemory)
func doit(bla:TestClass) {
s1?.add(bla)
bla.s1?.add(self)
}
deinit {
print("Deinit")
}
}
Now your second call will trigger deinit properly.