[Swift] Type type-Class sequel-

This article is a continuation of [Swift] Type Type-Class Part 1-. If you haven't seen the first part, please have a look.

Let's start with the continuation of the first part.

Elements associated with the class

As an element associated with the class itself, not as an instance of the class There are ** class properties ** and ** class methods **.

It has similar properties and is associated with the type itself rather than an instance of the type. There are static properties and static methods.

The difference between the two is ** Static properties and methods cannot be overridden, whereas Class properties and class methods can be overridden. ** **

Class properties

Class properties are not instances of the class It is a property associated with the class itself.

It is used when handling instance-independent values.

To define class properties Add the class keyword at the beginning when declaring the property.

Also, to access the class properties, Describe and access as type name.class property name.

In the following sample code className The class property is a property that returns the class name.

Because the class name is the same across all instances I think it is appropriate to define it as a class property.


class A {
    class var className: String {
        return "A"
    }
}

class B: A {
    override class var className: String {
        return "B"
    }
}

A.className   // A
B.className   // B

Class method

A class method is a method that is associated with the class itself, not an instance of the class. It is used when defining instance-independent processing.

To define a class method Just add the class keyword to the beginning of the method definition.

Also, when calling a class method It will be type name.class method name.

The inheritance () class method in the following sample code It is a class method that returns the inheritance relationship of the class.

Subclasses add their class name to the value of the superclass.


class A {
    class func inheritance() -> String {
        return "A"
    }
}

class B: A {
    override class func inheritance() -> String {
        return super.inheritance() + "<-B"
    }
}

class C: B {
    override class func inheritance() -> String {
        return super.inheritance() + "<-C"
    }
}

A.inheritance()   // A
B.inheritance()   // A<-B
C.inheritance()   // A<-B<-C

It's similar to static properties and methods, so I don't think I'm doing anything particularly difficult.

For those who find it difficult, static properties and See the here article that describes static methods.

Use static and class properly

Static properties and static methods It can also be used for classes.

In other words, if you want to define an element that is associated with the type itself rather than the ** instance for the class, Static properties, static methods and You can select both class properties and class methods. ** **

As I mentioned earlier, Static methods and properties cannot be overridden. In contrast, class properties and class methods can be overridden.

** So which one to choose is It depends on whether the value is subject to change in the subclass. ** **

Initializer type and initialization process

The initializer's role is to initialize all properties by the time the ** type instantiation is complete. Keeping type integrity ** was introduced in another article.

It's not that complicated if it's just a type of initializer, Since there is a ** inheritance relationship ** in the class, ** You need to ensure that the properties defined in the various hierarchies are initialized. ** **

There are three rules to achieve that, Before that, I would like to introduce the types of initializers.

The initializer is ** designated initializer ** It can be classified into two types: ** convenience store initializer **.

Designated initializer

The designated initializer is the main initializer for the class. ** All stored properties must be initialized in the specified initializer. ** **

The designated initializer is defined in the same way as the initializers that have appeared so far.


class Sample {
    let a: String
    let b: String
    
    init(a: String, b: String) {
        self.a = a
        self.b = b
    }
}

Convenience store initializer

Convenience initializer is an initializer that relays ** designated initializer. ** ** (I'm sorry for only katakana (laughs))

Convenience store initializer assembles arguments internally You need to call the designated initializer.

The definition method can be defined by adding the convenience keyword to the initializer.

In the sample code below I have created a class with three properties, a, b, and c.

The designated initializer takes all the arguments a, b, and c. Initialization is performed by assigning to each property.

convenience init(a: String, b: String) However, the convenience store initializer receives only two arguments, a and b.

self.init(a: a, b: b, c: a + b) Then you pass the value to your initializer. For a and b, we are passing the passed arguments as they are, but for c, we are passing the value of a + b.


class Sample {
    let a: String
    let b: String
    let c: String
    
    init(a: String, b: String, c: String) {
        self.a = a
        self.b = b
        self.c = c
    }
    
    convenience init(a: String, b: String) {
        self.init(a: a, b: b, c: a + b)
    }
    
    func printSample() {
        print("a: \(a)\nb: \(b)\nc: \(c)")
    }
}

let sampleA = Sample(a: "123", b: "456", c: "789")
let sampleB = Sample(a: "123", b: "456")

sampleA.printSample()
print("----")
sampleB.printSample()

Execution result
a: 123
b: 456
c: 789
----
a: 123
b: 456
c: 123456

Convenience store initializer ** It acts as a relay initializer to be passed to the initializer as described above. ** **

3 rules

To achieve type-consistent initialization The class initializer has the following three rules.

**-The designated initializer calls the superclass designated initializer. ・ Convenience store initializers call initializers of the same class. ・ Convenience store initializer finally calls designated initializer **

If this rule is met The specified initializer of all inherited classes is always executed, We guarantee that the properties defined in each class are initialized.

** Conversely, if there is a rule that does not meet even one A compilation error will occur because the types are not consistent. ** **

Guarantee type integrity, even a little Thanks to Swift for creating an environment with fewer errors ...

Default initializer

If the property does not exist or if all properties have been initialized No need to initialize in the specified initializer.

In that case, the default designated initializer is implicitly defined.


class A {
    let a = 10
    let b = 10
    
    //An initializer similar to the following is automatically defined
    // init() {}
}

If there is even one property that has not been initialized, You will need to initialize it in the specified initializer.

In that case, you need to explicitly define the designated initializer.

Class memory management

Swift uses a method called ARC for class memory management.

ARC is an abbreviation for ** Automatic Reference Counting **. The literal translation is ** automatic reference count **, but it is the method as the name implies.

In ARC, every time you instantiate a class Automatically allocates memory area for that instance.

In other words, the more instances you have, the more memory you will use.

So ** Automatically release instances you no longer need, Let's reduce the memory area as much as possible! It becomes the method. ** **

So when do you decide when an instance is no longer needed? This is where automatic reference counting comes into play.

** In ARC, to prevent the (necessary) instances in use from being released References from properties, constants, and variables to instances of each class It automatically counts how many exist. ** **

When this count reaches 0 ** The instance is not referenced by anyone, so memory is freed. ** **

This count is called the reference count.

Denisher

When the instance is destroyed by ARC, The class's de-initializer runs.

The deinitializer is the opposite of the initializer, ** It will perform termination processing such as cleanup. ** **

The definition method is as follows.


class class name{
   deinit {
Termination processing such as cleanup
   }
}

I would like to conclude the explanation of the class. The first part and the second part have become longer, but thank you for watching until the end.

If you can use class inheritance and initializers well, I think that the amount of code written will be reduced and code with high readability and security will be applied.

Let's deepen our knowledge about the class!

There are other articles that describe the types of molds, so be sure to check them out!

[Swift] Type type ~ Basic knowledge ~[Swift] Type type ~ Structure ~[Swift] Type type ~ Enumeration ~

Thank you for watching until the end.

Recommended Posts

[Swift] Type type-Class sequel-
[Swift] Type type-Class first part-
[Swift] Type type ~ Enumeration type ~
[Swift] Type type ~ Structure ~
[Swift] Type components ~ Type nesting ~
[Swift] Type component ~ Subscript ~
[Swift] Type design guidelines
[Swift] Shared enumeration type
[Swift] Type component ~ Method ~
[Swift] Summary about Bool type
[Swift] Type component ~ Property Part 2 ~
Great Swift pointer type commentary
[Swift] Converts a UInt64 type integer to [UInt8]