I'm pleased to introduce guest blogger Jennifer Black, manager of the MATLAB object system team. Today Jennifer will be sharing some thoughts on what happens when objects are destroyed in MATLAB and how you can control aspects of this process.
When an instance of a class is cleared, MATLAB automatically cleans up the values of the properties owned by that object. For example, if I have a Signal class with a property that stores a matrix of double values, that matrix is destroyed when a Signal is destroyed. Sometimes, however, additional actions might be needed to handle external resources.
Let’s consider the following class:
classdef FileReader1 < handle properties FileID end end
If I have an open FileReader named reader in my workspace, clearing reader will cause MATLAB to clear the value stored in my FileID property. Now the number is gone, but the file is still open. Clearing FileID without first closing the file means the file is left open even though it is no longer being used. If this happens enough times I might run out of system file handles.
Let's look at defining a destructor to close the file handle. A destructor is a method of a class responsible for cleaning up resources owned by objects. In MATLAB, the handle superclass is used for all kinds of objects that have a unique identity independent of their current state. Unlike numbers, matrices, etc., handle objects represent unique things that have a beginning and an end and may change internal state along the way. Any subclass of handle may define a special method named delete, often referred to as a destructor. In the case of my FileReader class, I can correct the problem of leaking file handles by implementing my own destructor:
classdef FileReader2 < handle properties FileID end methods function delete(readerObj) fclose(readerObj.FileID); end end end
A MATLAB destructor takes a single input argument - the object being destroyed - and returns no outputs. The input object is always scalar even if an array of MATLAB objects goes out of scope all at once.
In a MATLAB class you can define a method named delete that is not a destructor. A delete method with a different signature is not a destructor, nor is a static delete method. For example, defining the method to take more than one input argument or to return any output arguments means the method is not treated as an object destructor.
An object destructor can be called either explicitly by calling the delete method, or implicitly by MATLAB, for example when a variable is cleared or goes out of scope. Let's consider the difference between these two actions. To do so I will add a constructor and a readData method to my class:
classdef FileReader3 < handle properties(SetAccess = protected) FileID = -1; FileName end methods function reader = FileReader3(fname) if ischar(fname) reader.FileName = fname; reader.FileID = fopen(fname,'r'); end end function colorData = readData(reader) if reader.FileID == -1 error('No file name has been specified for this FileReader. No data will be read.'); else colorData = fscanf(reader.FileID,'%f',[3,inf]); colorData = colorData'; frewind(reader.FileID); end end function delete(reader) if reader.FileID ~= -1 s = sprintf('Closing %s', reader.FileName); disp(s); fclose(reader.FileID); end end end end
First, let's consider the case where I have one variable holding a FileReader:
myReader = FileReader3('colorData.txt');
Explicitly calling delete on myReader invokes the destructor and then destroys the FileReader. The myReader variable remains in my workspace, but its handle is no longer valid:
ans = 0
An implicit call to a destructor will occur if I clear myReader from my workspace:
myReader = FileReader3('colorData.txt'); clear myReader
The destructor will also be implicitly called if my variable goes out of scope, for example because the end of a function has been reached. To illustrate, let's add a helper function in which I create a FileReader:
function colorData = readDataFromFile(filename) myReader = FileReader3(filename); colorData = readData(myReader); end
When I call my new helper function, myReader is created in the function and will go out of scope when the function ends. This causes MATLAB to clear the variable, which results in an implicit call to the delete method:
colordata = readDataFromFile('colorData.txt');
Now, let's contrast what we have just discussed about having a single handle with the case where I have multiple handles to the same FileReader. This happens, for example, when I create a handle object and then assign that handle to another workspace variable:
reader1 = FileReader3('colorData.txt'); reader2 = reader1;
With these two handles to the same FileReader now in my workspace, I will explicitly call delete, and then use the isvalid method to see what happened to the FileReader object:
ans = 0
ans = 0
As expected, the reader1 handle is no longer valid, but note that reader2 is also no longer valid. Why did this happen? Because when I explicitly call delete, the destructor is called and the object is destroyed no matter how many handles there are referencing that object.
Now let's see what happens when a destructor is called implicitly. Once again let's create two handles to the same FileReader:
reader1 = FileReader3('colorData.txt'); reader2 = reader1;
But this time, rather than call the destructor explicitly, I will just clear one of the handles. As we saw earlier, this can result in an implicit call to the delete method:
clear reader1; isvalid(reader2)
ans = 1
Why is reader2 still valid, and why was my destructor not called? Because MATLAB will only implicitly call a destructor when the last reference to an object is cleared. As reader2 still exists in my workspace, the underlying FileReader is not destroyed.
Most often a destructor is defined as a public method, but it can also be declared as a private or protected method. Making it private will prevent code outside the class from explicitly calling the destructor. Similarly, a protected destructor can only be explictly called from methods of the same class or from subclass methods. MATLAB will always be able to implicitly call a destructor, even one declared private or protected. You might choose to restrict access to your destructor in a situation where you want to prevent the accidental deletion of an object, such as when you have a singleton object.
What happens when a handle is stored as a field of a struct or a cell of a cell array, or as a property of another object? When that top-level container variable is destroyed, the handle object will also be destroyed and its delete method implicitly called if and only if no other references to the object exist. Let's look at an example with structs:
s.myReader = FileReader3('colorData.txt'); clear s
Note that the FileReader stored in the myReader field of s has been destroyed. However, as we saw previously, another handle to the same FileReader will prevent the destruction of the object:
reader4 = FileReader3('colorData.txt'); s.myReader = reader4; clear s isvalid(reader4)
ans = 1
Even after clearing s, we see that reader4 is still valid. The second handle prevented the implicit destruction of the object.
So far we have been looking at examples of stand-alone classes, but what about classes that are part of a hierarchy? In MATLAB, every class has the ability to control its own destruction behavior. In a hierarchy of classes, every class can define its own destructor. As a result, a destructor cannot be defined as a Sealed method.
When an object is destroyed, MATLAB will call the destructor of the class of the object first, if it has been defined. Next, the destructor of each superclass is called, starting with the most direct superclass.
A destructor can reference the properties of the class itself, including properties inherited from superclasses, but it generally should not reference properties of a subclass. Why? Because when a superclass destructor is executing, the destructor of its subclass has already executed, and could have invalidated the values in its properties.
Now that we've discussed the basics of working with destructor methods in MATLAB, I'd like to hear of your experiences. Are you already using destructors? If you have any interesting applications or questions, I'd be very happy to hear about them here.