deinit이란?

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


deinit이란?

Deinitialization: 소멸자

소멸자는 클래스 인스턴스가 메모리에서 해제될때 즉시 호출되는 함수이다.
swift의 소멸자는 deinit 키워드를 이용해 사용할 수 있다. 작성방식은 init과 비슷하다!
swift의 소멸자는 구조체가 아닌 클래스 타입에서만 작성이 가능하다.

작동방식

swift는 특정 인스턴스가 더이상 필요없을때 메모리 공간 확보를 위해 해당 인스턴스를 자동적으로 메모리 해제시킨다.
이를 우리가 아는 ARC라고 하고 이 ARC는 전형적으로 특정클래스 인스턴스를 메머리 상에서 해제하고 싶을 때 수동적으로 해당 인스턴스를 정리할 필요가 없게 만든다. 즉 ARC는 더이상 필요없는 인스턴스에 대해서 자동으로 메모리 할당/해제 코드를 실행해준다. 하지만 앱이 자원을 사용하며 작동되고 있을때에는 개인적으로 따로 메모리를 관리해줄 필요가 있다.

클래스의 소멸자는 하나의 클래스 인스턴스 당 최대 하나씩 존재한다. 이러한 소멸자는 별도의 매개변수나 괄호를 명시하지 않는다.

deinit {
  // perform the deinitialization
}

소멸자는 해당 클래스 인스턴스의 메모리 해제가 필요할 때 자동으로 호출된다.
즉, 인스턴스가 메모리 해제되기 직전에 호출된다.

해당 소멸자를 직접 호출하는것은 허용하지 않는다.
상위 클래스의 소멸자는 그들의 서브클래스에 의해 상속된다. 또한 상위 클래스 소멸자는 자동적으로 마지막 서브클래스의 소멸자 구현부에서 호출된다.
상위 클래스의 소멸자는 항상 호출되는데, 아래의 서브클래스 소멸자에서 구현이 없을때에도 호출된다.

소멸자가 호출되기 전까지 인스턴스는 메모리에서 해제되지 않기 때문에 소멸자는 호출되는 인스턴스의 모든 프로퍼티에 접근이 가능하다.
또한 해당 프로퍼티들에 기반한 행위의 변경 또한 가능하다.

class Person {
  var name: String
  var age: Int

  init(name: String, age: Int) {
    self.name = name
    self.age = age
  }

  deinit {
    print("deinit person class instance")
  }
}

// init call
var zehye = Person(name:"zehye", age: 20)

// deinit call
zehye = nil
  • zehye는 Person 클래스의 인스턴스다.
  • 인스턴스 생성 후 해당 인스턴스에 nil을 할당함으로써 메모리를 해제한다.
  • nil을 할당하면 deinit 소멸자가 호출된다.
  • deinitd에 구현해놓은 print()가 호출된다.
  • 콘손에 해당 문구가 출력된다.

그런데 만약 특정 클래스의 인스턴스 사용이 필요없어져 해체를 하고싶은데 deinit이 호출되지 않았다면 이때는 강한참조순환을 의심해보자!
이럴때에는 deinit메서드에 콘솔 출력코드를 두어 메모리 해제여부를 확인해보는것이 방법이다.

static과 class 제대로 알아보자

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


  • 함수: func이라는 키워드로 생성하는 것 모두 함수
  • 메서드: 클래스, 구조체, 열거형 속에 포함되는 함수

instance method

인스턴스와 관련된 메서드로 인스턴스를 생성해야만 호출이 가능하다.

class Person {
  func getName() {
    print("\(name)")
  }
}

이렇게 아무런 수식어 없이 func 으로 시작하는 메서드 모두 인스턴스 메서드라고 한다.
이러한 인스턴스 메서드를 호출하기 위해서는 아래와 같은 방법을 사용한다.

  1. Person이라는 인스턴스를 먼저 생성하고
  2. 생성된 인스턴스에 .을 사용해 메서드에 접근한다.

즉, 인스턴스 메서드는 인스턴스와 연관된 메서드이기 때문에, 인스턴스를 생성해야만 인스턴스를 통해 메서드에 접근할 수 있으며 아무런 수식어없이 func으로 선언된 메서드는 모두 인스턴스 메서드를 의미한다.

type method

형식(type)과 관련된 메서드로, 인스턴스 생성없이 형식(Type)의 이름만 알면 호출이 가능하다.
메서드 선언시 func 이란 키워드 앞에 static혹은 class가 붙으면 그 메서드는 타입 메서드이다.

class Person {
  static func getName() {
    print("\(name)")
  }

  class func getAge() {
    print("\(age)")
  }
}

위와 같은 메서드는 모두 타입 메서드이다.
이러한 타입메서드를 호출하기 위한 방법은 아래와 같다.

  1. 타입메서드는 인스턴스와는 전혀 상관없기때문에 Person이라는 Type에만 연관되어있다.
  2. 따라서 Person이라는 type 이름을 호출한다.
Person.getName()
Person.getAge()

타입메서드는 인스턴스 메서드가 아니기 때문에, 인스턴스를 생성할 필요도 없으며
오로지 자신이 속해있는 type의 이름인 Person만 알면 그 Person을 통해 메서드를 호출할 수 있게 된다.

static? class?

타입메서드를 작성하기 위해서는 메서드 앞에 static 혹은 class를 적어주면 되는데,
이둘을 적어주는 궁극적인 차이는 메서드 오버라이딩(override)의 여부이다.

1. static : 오버라이딩을 금지함

static은 항상 서브클래스에서 해당 타입 메서드를 오버라이딩하는 것을 금지 한다.

class Person {
  static func getName() {
    print("name")
  }
}
class PersonPerson: Person {
  override static func getName()  // cannot override static method
}

이렇게 static으로 선언된 타입 메서드 getName의 경우 서브클래스에서 오버라이드 하려는 경우 에러가 발생한다.

2. class : 오버라이딩을 허용

class는 서브클래스에서 타입 메서드 오버라이딩을 허용 한다.

class Person {
  class func getName() {
    print("name")
  }
}
class PersonPerson: Person {
  override class func getName()
}

이렇게 class로 선언된 타입메서드의 getName의 경우 서브클래스에서 오버라이딩을 해도 에러가 발생하지 않는다.

타입 메서드와 인스턴스 메서드의 멤버 접근 범위

  • 일반 프로퍼티: 저장 프로퍼티 & 연산 프로퍼티
  • 타입 프로퍼티: 저장 타입 프로퍼티 & 연산 타입 프로퍼티
class Person {
  let name = "zehye"  // 저장 프로퍼티
  static let alias = "zehyezehye"  // 저장 타입 프로퍼티
}

이렇게 name이라는 저장 프로퍼티와 alias라는 저장 타입 프로퍼티가 있을때 인스턴스 메서드와 타입 메서드 모두 이 둘을 사용할 수 있을까?

  1. 타입메서드: 타입 멤버(프로퍼티&메서드)만 사용가능하고, 같은 타입멤버는 이름없이 접근 가능
  2. 인스턴스 메서드: 인스턴스 멤버(프로퍼티&메서드)를 사용할 수 있고, 타입 멤버도 타입만 알면 접근 가능

타입메서드의 경우

class Person {
  let name = "zehye"
  static let alias = "zehyezehye"

  static func sayHi() {
    print(name)  // error! Instance member 'name' cannot be used on type 'Person'
    print(alias)
  }
}

저장 프로퍼티인 name은 인스턴스멤버이다.
따라서 인스턴스가 생성될때마다 메모리에 올라가는 인스턴스와 연관된 프로퍼티이다.

하지만 타입메서드는 인스턴스를 선언할 필요가 없는 메서드이기 때문에 자신과 같이 인스턴스를 생성할 필요없는 타입 프로퍼티의 경우, 같은 타입에 한해 타입 이름없이 접근이 가능 하지만 인스턴스가 생성되어야만 저장공간을 갖는 인스턴스 멤버에는 접근이 불가능하다.

인스턴스 메서드의 경우

class Person {
  let name = "zehye"
  static let alias = "zehyezehye"

  static func sayHi() {
    print(name)  
    print(Person.alias)
  }
}

반대로 인스턴스를 생성해야만 접근이 가능한 인스턴스 메서드에서는 인스턴스 멤버인 name에는 당연하게 접근이 가능하고(인스턴스 생성이 됐으니 해당 인스턴스 멤버인 name도 메모리에 올라감) 인스턴스와 상관없는 타입 멤버에 접근시 기존 방식과 같이 타입 이름을 통해 접근이 가능 하다.

legacy란?

|

개인 공부 내용을 정리한 글입니다.
잘못된 내용이 있다면 편하게 댓글 남겨주세요!


legacy

개발자 친구들과 대화를 하다보면 레거시, 레거시하며 레거시라는 단어를 많이 들어볼 수 있을 것이다.
그렇다면 레거시라는 건 무슨 말을 의미하는 것일까?

레거시 프로그램, 레거시 데이터, 레거시 코드와 같은 말속의 레거시라는 것은 프로그래밍 언어, 플랫폼 그리고 기술 등에 있어서 과거로부터 물려 내려온 것을 의미한다. 컴퓨터를 사용하는 대부분의 기업들은 중요한 업무를 처리하는 레거시 응용 프로그램과 데이터베이스들을 가지고 있다. 즉, 레거시 시스템은 낡은 기술이나 방법론, 컴퓨터 시스템, 소프트웨어 등을 말한다.

말이 너무 어려운데, 그냥 간단하게 말하면 아래와 같다.

레거시의 사전적인 의미는 (남이 남긴)유산으로 해석할 수 있다.
레거시 코드란 다른 사람에게 넘겨받은 읽기 어렵고 수정하기 오래된 어려운 코드

기술의 변화가 많은 프로젝트들에 있어서 시간이 지나면서 기술이 발전하고 그러면 그럴수록 레거시 코드들이 점점 쌓여만간다.
그렇다면 프로젝트 내에서 레거시 코드들이 많아진다면 겪을 문제점은 무엇이 있을까?

1. 해당 코드를 이해하기 어려운 문제점 발생 > 변수명, 메소드명이 명확하지 않거나 너무 복잡한 경우
2. 해당 코드에 대한 완벽한 이해가 어렵기 때문에 수정이 필요한 곳에 정확한 수정을 하기 어려움
2-1. 수정했다하더라도 이에 발생할 사이드이펙트가 어디에서 발생할지에 대한 예측도 어려움
3. 코드에 대해 이해가 어려워지다보니 해당 프로젝트에 대한 유지보수 작업을 진행하는 데에 있어 어려움

레거시 코드를 만들지 않기 위해서는 어떻게 해야하나?

1. magic number 사용을 하지 않기

if(item == 1 || item == 2) {
  Item.remove(item)
}

private let book = 1
private let pensil = 2

if(item == book || item == pensil) {
  Item.remove(item)
}

첫번째 방식의 경우 숫자 1, 2가 무엇을 의미하는지 직관적으로 알수 없지만, 아래와 같이 코드를 작성하면 직관적으로 각각이 무엇을 의미하는지 알아보기 쉽기 때문에 아래와 같은 방법을 사용하는것이 좋다.

2. 의도를 나타내는 이름을 사용하기

3. if문이 덕지덕지 붙어있는 복잡한 조건식을 피하자

외는 이 블로그를 통해 확인해보자! 참고한 블로그

alpha, opaque, opacity의 차이란?

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


alpha, opaque, opacity

1. alpha: 0.0 ~ 1.0 범위의 값으로 0.0은 완전하게 투명, 1.0은 완전하게 불투명

2. isOpaque: view가 투명한지 불투명한지 결정하는 Bool값.

  • true: drawing 시스템이 view를 완전하게 불투명하게 처리 > 성능향상에 좋음
  • false: 투명하게 처리

3. opacity: 불투명도 0.0(투명) ~ 1.0(불투명)범위내에 있어야 하며, 해당 범위는 벗어나는 값은 최소값 또는 최대값으로 고정.

  • 기본값은 1.0이며 alpha와의 차이는 alpha는 UIView에, opacity는 CALayer의 프로퍼티이다.

final 키워드를 사용하는 이유

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


final

타 프로그래밍 언어와 마찬가지고 swift 언어 또한 클래스의 상속을 지원한다. 더이상 해당 클래스에서 하위 클래스로의 전체 클래스 또는 일부(메서드, 프로퍼티)가 상속에 따른 재정의가 될 필요가 없다고 판단한 경우 final키워드를 각 요소 앞에 추가함으로써 상속을 방지할 수 있다.

  • 재정의할 필요가 없을때 사용
  • 클래스 자체가 상속되는것을 막고싶을 때 사용 > final 클래스를 상속받으려는 클래스는 컴파일 시 에러가 발생함

이 밑의 개념은 아직 내가 잘 이해를 못한 것 같다.
이해가 되면 이해한 만큼 다시 정리해봐야할 것 같다….ㅠ

vtable

vtable은 가상 메서드 테이블을 이르는 것으로, 메서드 오버라이딩에 따라 실행 시점에 어떤 메서드를 실행할 지 결정하는 동적 디스패치를 지원하기 위해 프로그래밍 언어에서 사용하는 매커니즘이다.

final 키워드 적용이 성능에 미치는 영향

  • final로 선언된 요소들은 직접 호출
  • 그렇지 않은 경우는 vtable을 통해 간접 호출

오버라이딩한 메서드는 실행 시점에 어떤 메서드를 실행할 지 결정하는 반면 final 키워드가 적용된 메서드는 컴파일 시점에 어떤 메서드를 실행할 지 결정할 수 있기 때문에 성능 상 이점을 가진다고 한다.