The deinitializer of a class is not called when initialization is aborted by raising an error.
The Language Reference & Guide
- Welcome to Emojicode
- Syntax
- The Basics
- Literals
- Variables and Assignment
- Control Flow
- Classes & Value Types
- Overloading
- Operators
- Optionals
- Errors
- Inheritance and Overriding
- Protocols
- Enumerations
- Types and Namespaces
- Types as Values
- Documentation
- Generics
- Callables
- Packages
- Threads
- Safe and Unsafe Code
- Memory Management
- References
- Appendix: The Emojicode Compiler
Memory Management
Hereโs the good news about memory management in Emojicode: Emojicode does the hard work. This chapter describes the mechanisms employed.
Lifetime of Temporary Values
Emojicode manages memory with reference counting. This means that Emojicode maintains a count of how many references exist to every object. An object is destroyed immediately once there are no more references to it.
Consider the following example:
๐๐ ๐คShawn๐คโ๏ธ
The above code sample creates an instance of the class ๐. Since this object is not assigned to any variable or returned, we call it a temporary value. Temporary values are destroyed at the end of the statement in the order they were created.
Letโs have a look at this more complicated example:
๐ ๐ฆ ๐
๐ pet ๐ ๐๐
๐
๐ ๐ ๐
๐๐ name ๐ก
๐ ๐ผ name ๐ก ๐๐
๐
๐ ๐
๐๐ฆ ๐๐ ๐คShawn๐คโ๏ธโ
๐
Here we create an instance of ๐ฆ, which we pass an instance of ๐ to. ๐ฆ does not anything with the fish instance (like assigning it to an instance variable). So both the fish instance and the gorilla instance will be destroyed at the end of the statement. Because the fish instance was created first, it will be destroyed first.
Borrowing and Escaping Use
Emojicode supports the notion of borrowing and escaping use of a value.
This concept only applies to the use of method or initializer parameters and the use of the context, i.e. the value returned by ๐, in the method or initializer.
A value is considered escaping, if it (or a copy of it) can outlive the call of the method or initializer. Consider, for instance, this class:
๐ ๐ฅง ๐
๐ญ ...
โ๏ธ ๐ ๐
๐ญ ...
๐
๐
๐ ๐ฆก ๐
๐๐ pie ๐ฅง
๐ ๐ผ pie ๐ฅง ๐๐
๐
It is obvious that the pie reference passed to the ๐ initializer of ๐ฆก will outlive the call as it is assigned to an instance variable. This parameter is considered escaping, therefore. On the other hand, the below class method does not use its parameter in an escaping way:
๐ ๐ ๐
๐โ๏ธ ๐ pie ๐ฅง ๐
๐ pieโ๏ธ
๐
๐
No copy of pie
is made here that will outlive the call of ๐.
As mentioned before, a method itself can be escaping, if it makes the this context outlive the call. The following is an example of such a method:
๐ ๐ฅง ๐
โ๏ธ ๐ฒ โก๏ธ ๐ฆก ๐
โฉ๏ธ ๐๐ฆก ๐โ๏ธ
๐
๐
๐ is passed to an escaping parameter in this example, which obviously causes the value to escape.
Simply put, there are four ways in which a value can escape:
- The value is assigned to an instance variable.
- The value is passed to an escaping parameter.
- An escaping method is called on the value.
- Return the value.
When compiling, the Emojicode compiler analyses all methods to determine whether they just borrow a value or let it escape. If you generate an interface file for a package, you can see all escaping parameters and methods annotated with ๐๐ฅก. As an example, take a look at ๐จโs ๐ป:
๐ ๐ ๐จ๐Element โช๏ธ ๐๐
๐ Appends `item` to the end of the list in `O(1)`. ๐
๐ โ๏ธ ๐ป ๐๐ฅก item Element
๐
Obviously, appending a value to a list causes the value to escape, which the compiler correctly determined and annotated the parameter with ๐๐ฅก.
In principle, you can manually annotate parameters and methods with ๐๐ฅก, but unless you build a package with methods implemented in another language, there is no reason to do so.
Deinitializers
Emojicode allows you to define a deinitializer for your classes. A deinitializer is a function that is executed right before a class instance is destroyed. Its syntax is this:
deinitializer โถ โป๏ธ block
We can define a deinitializer for the ๐ฆ and ๐ class, to prove the behavior we have talked about before:
๐ ๐ฆ ๐
๐ pet ๐ ๐๐
โป๏ธ ๐
๐ ๐คGorilla says Bye-Bye๐คโ๏ธ
๐
๐
๐ ๐ ๐
๐๐ name ๐ก
๐ ๐ผ name ๐ก ๐๐
โป๏ธ ๐
๐ ๐คFish deinit!๐คโ๏ธ
๐
๐
๐ ๐
๐๐ฆ ๐๐ ๐คShawn๐คโ๏ธโ
๐
As expected, running the above code results in:
Fish deinit!
Gorilla says Bye-Bye
You can only specify a deinitalizers for classes.
Lifetime in General
We will now adjust our program and make the gorilla actually store its pet:
๐ ๐ฆ ๐
๐๐ pet ๐
๐ ๐ผ pet ๐ ๐๐
โป๏ธ ๐
๐ ๐คGorilla says Bye-Bye๐คโ๏ธ
๐
๐
If we kept the rest of the program the same, the output will still change:
Gorilla says Bye-Bye
Fish deinit!
That is because the gorilla now maintains a reference to the fish. Thus, the compiler cannot write code to immediately destroy the fish at the end of the statement. But the gorilla can be destroyed as before. But once the gorilla is gone, also our reference to the fish is gone, so the fish is destroyed then.
The same is true when working with value types, like in the following example.
๐ ๐ณ ๐
๐๐ fish ๐
๐ ๐ผ fish ๐ ๐๐
๐
๐ ๐ ๐
๐๐ name ๐ก
๐ ๐ผ name ๐ก ๐๐
โป๏ธ ๐
๐ ๐คFish deinit!๐คโ๏ธ
๐
๐
๐ ๐
๐๐ณ ๐๐ ๐คShawn๐คโ๏ธโ
๐
We can, however, update our program slightly to see a change:
๐ ๐
๐๐ณ ๐๐ ๐คShawn๐คโ๏ธโ โก๏ธ card
๐ ๐ค๐๐คโ๏ธ
๐
Running this produces:
๐
Fish deinit!
You can see that the ๐ณ is not immediately destroyed because we copied it into a variable. The variable is destroyed at the end of the scope, so is the ๐ณ in it. Hence we first see ๐ and then Fish deinit!
.
Weak References
In the case of a circular reference, automatic reference counting cannot detect objects that should be deleted. A circular reference occurs if objects point at each other in a circle.
Circular references can be worked around by so called weak references. Weak references are not taken into account when counting the references left to an object and thus allow breaking up circular references.
Consider this program as an example:
๐ ๐ ๐
๐๐ moon ๐
๐ ๐ผ moon ๐ ๐
๐ โก๏ธ ๐moonโ๏ธ
๐
โป๏ธ ๐
๐ ๐คEarth deinit๐คโ๏ธ
๐
๐
๐ ๐ ๐
๐๐ earth ๐ฌ๐
๐ ๐๐
โก๏ธ๐ new_earth ๐ ๐
new_earth โก๏ธ ๐earth
๐
โป๏ธ ๐
๐ ๐คMoon deinit๐คโ๏ธ
๐
๐
๐๐
๐๐ ๐๐โ๏ธโ๏ธ
๐
When run, the program will exit without ever printing โEarth deinitโ or โMoon deinitโ as the ๐ and ๐ instance a pointer at each other. Neither of them can be deleted as both have a reference count of one.
The solution is using a weak reference in one of the classes:
๐ ๐ ๐
๐๐ earth ๐ฌ๐ถ๐๐๐
๐ ๐๐
โก๏ธ๐ new_earth ๐ ๐
๐๐ถnew_earthโ๏ธ โก๏ธ ๐earth
๐
โป๏ธ ๐
๐ ๐คMoon deinit๐คโ๏ธ
๐
๐
In the above program the reference from the moon back to the earth does not count when determining whether the earth instance can be deleted.
The program prints:
Earth deinit
Moon deinit
Weak references are part of the s package. See the package documentation to learn more about their usage.
Detecting Shared Values
In some cases, you might want to know whether your reference to a value is unique. This can be done by using the ๐ฎ operator on the variable holding the reference.
unique โถ ๐ฎ variable