클래스의 특성에는 initializer(생성자), self, inheritance(상속)가 있다. 

init( )

클래스는 객체생성 시 자동으로 단 한번만 생성자 메소드를 호출한다. 스위프트에서는 init()이라는 이름으로 사용한다. 

주로 클래스 멤버변수 초기화의 용도로 사용을 한다.

class AAA{
    var a = 10
    var b = "아기상어"
    
    init(aa:Int, bb:String){s
        //생성자 --> 멤버변수 초기화로 주로 사용한다.
        print("AAA initializer start: \(aa), \(bb)")
        a = aa
        b = bb
    }
    
    func fn_1(){
        print("AAA fn_1() start: \(a), \(b)")
    }
    
    func fn_2(){
        print("AAA fn_2() start: ")
    }
}
var cc1 = AAA(aa:1234, bb:"엄마상어")
var cc2 = AAA(aa:456, bb: "할아버지상어")
cc1.fn_1()
cc2.fn_1()

init()은 따로 호출하지 않아도 알아서 실행이 되는 모습을 볼 수 있다. 예시와 같이 생성자를 통해 객체 생성과 동시에 변수의 초기화가 이루어지니 더 간결하게 코드를 적을 수 있다.

var arr = [AAA(aa:10, bb:"정우성"),
           AAA(aa:20, bb:"정좌성"),
           AAA(aa:60, bb:"정남성"),
           AAA(aa:80, bb:"정중성")]

for qq in arr{
    qq.fn_1()   //배열로 담아 메소드 실행
}

배열을 이용하여 메소드를 실행할 수 있다는 점도 알아두자.


self

self는 멤버요소 접근자로서 자기 자신 클래스 내 멤버변수에 접근할 때 사용된다.

class AAA{
    var a:Int!      
    var b:String!   
    
    init(_ a:Int, _ b:String){
        self.a = a  //self -> 현재 인스턴스의 멤버요소 접근자.
        self.b = b  //매개변수와 멤버변수의 혼동을 막기위해 명시적으로 선언하기 위한 용도로도 쓰인다.
        
        ppp()
    }
    
    func ppp(){
        print("a:\(a!), b:\(b!)")
    }
}
var zx = AAA(123, "조인성") //한번만 사용할 것이라면, 굳이 변수로 담지 않아도 된다.

위 예제는 init()의 매개변수와 클래스내의 멤버변수가 이름이 같아 혼동이 될수 있기에 명시적인 목적으로 사용했다.


self 예제 1

각 선분의 길이를 입력받으면 어떤 도형인지, 도형의 넓이, 도형의 둘레를 측정하는 클래스를 작성해보았다. 편의상 사각형은 직각사각형, 삼각형은 직각삼각형으로 취급했다.

class Shape{
    var name:String!
    var line:[Int]!
    var area = 0, border = 0
    
    init(_ line:Int...){    //사용자 정의 생성자 -- 필요한 멤버요소 획득
        self.line = line
        name = ["원","직사각형","직각삼각형"][line.count-1]   //line.count-1은 인덱스를 담당하게된다.
        calc()  //연산
    }
    
    func calc(){
        if name == "원"{
            let pi = 3.14
            self.area = Int(Double(self.line[0]) * Double(self.line[0]) * pi)
            self.border = Int(Double(self.line[0]) * 2.0 * pi)
        } else if name == "직사각형"{
            area = line[0] * line[1]
            border = (line[0] + line[1]) * 2
        } else if name == "직각삼각형"{
            area = line[0] * line[1] / 2
            border = line[0] + line[1] + line[2]
        }
    }
        
    func ppp(){
        print("\(name!): \(area), \(border)")
    }
}
var shapes = [
    Shape(5),
    Shape(5,6),
    Shape(5,6,8),
    Shape(10,20),
    Shape(14,16,21),
    Shape(8)
]

for sh in shapes{
    sh.ppp()
}


self 예제 2

학생클래스를 정의하고 학생 정보를 출력해보았다.

클래스명: Stud

입력내용: 이름,국어,영어,수학

출력정보: 이름,국어,영어,수학,총점,평균

class Stud{
    var name:String
    var kor:Int
    var eng:Int
    var math:Int
    
    var tot = 0
    var avg = 0
    
    init(_ name:String, _ scores:Int...){
        self.name = name
        self.kor = scores[0]
        self.eng = scores[1]
        self.math = scores[2]
        
        calc()
    }
    
    func calc(){
        tot = kor + eng + math
        avg = tot/3
    }
    
    func ppp(){
        print("name:\(name)\t국어:\(kor)\t영어:\(eng)\t수학:\(math)\t총점:\(tot)\t평균:\(avg)")
    }
}
var studs = [Stud("라이언",86,87,89),
            Stud("어피치",77,76,74),
            Stud("프로도",65,66,68),
            Stud("제이지",99,97,95)
            ]

for st in studs{
    st.ppp()
}


Inheritance

Inheritance(상속)는 상위클래스의 요소를 자식클래스가 가져와 사용할 수 있는 객체지향언어의 가장 큰 특징이다. 

상속의 선언은 다음과 같다. 

     class 자식클래스 : 부모클래스 {

          실행코드 ... 

     }

자식클래스는 부모클래스의 멤버변수와 메소드를 사용할 수 있는데, 메소드는 자식클래스의 필요에 따라 overriding(재정의)이 가능하다. 상속을 이용하는 가장 큰 이유이며 이렇게 하나의 메소드가 호출하는 주체에 따라 다르게 동작하는 것을 polimolphism(다형성)이라고 한다.

class Par{
    var a = 10
    var b = "부모"
    
    func fn_1(){
        print("부모 fn_1()")
    }

    func fn_2(){
        print("부모 fn_2()")
    }
}

class Child : Par{  //상속관계 정의(자식 : 부모)
    var c = 3000
    
    override func fn_2(){   //overriding 재정의. 부모의 메소드를 재정의하여 사용한다.
        print("자식 fn_2()")
    }
    
    func fn_3(){
        print("자식 fn_3()")
    }
    
}

class Child2 : Par{  //상속관계 정의(자식 : 부모)
    var d = 4040
    
    func fn_4(){
        print("자식2 fn_4()")
    }
}
var pp = Par()
var cc = Child()
var cc2 = Child2()

print("pp-----------")
print(pp.a,pp.b)
pp.fn_1()
pp.fn_2()

print("cc-----------")
print(cc.c)
print(cc.a,cc.b,cc.c)
cc.fn_3()
cc.fn_1()
cc.fn_2()

print("cc2-----------")
print(cc2.d)
//print(cc2.c)    //Child클래스의 멤버변수에 접근 불가.
print(cc2.a,cc2.b,cc2.d)
//cc2.fn_3()  //Child클래스의 메소드에 접근 불가.
cc2.fn_4()
cc2.fn_1()
cc2.fn_2()

예제를 보면 알 수 있듯이 자식 클래스들은 모두 부모 클래스에 접근이 가능하지만, 서로 다른 자식클래스들은 서로 간에 접근이 불가능하다. 또한 같은 함수를 호출하더라도 overriding이 되어있는지의 여부에 따라 다른 결과물이 출력되는 점도 확인 할 수 있다.


객체지향언어의 꽃이라고 볼 수 있는 상속은 누군가 체계적으로 작성한 클래스를 상속, 재정의하여 본인에게 알맞게 사용 가능하다는 점이 매력적인 기능이다.

'iOS > TJ' 카테고리의 다른 글

day12_funcVariable, selfCall, class  (0) 2021.06.29
day11_param, return, funcCall  (0) 2021.06.18
day10_func()  (0) 2021.06.17
day09_dictionary, set  (0) 2021.06.16
day08_multiArray, tuple  (0) 2021.06.03

+ Recent posts