ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 메모리 관리 RC, Reference Counting
    개발...................../TIL 2024. 7. 10. 11:32

    컴맹이어서 메모리가 뭐고, RAM이 몇이냐고 물어봤을 때 넹??그게 몬가요,,;; 내가 앱을 만들고자 하니 메모리 관리의 필요성이 있으니 수년간 이해해보려던 메모리와 디스크에 대해 이번에야 저장됐다. 늘 메모리에 있다가 디스크로 넘어간..^^암아도..? 

     

    메모리

    • 일반적으로 RAM 을 말하는 경우가 많다.
    • 맥북에서도 몇 GB 짜리 RAM 을 사용하는지 볼 수 있다.
    • RAM 은 휘발성 메모리이다. 즉, 데이터를 영구적으로 저장하지 않는다. 일시적인 저장에 사용한다.→ 앱도 결국 데이터 덩어리이기 때문에, 실행을 시키면 메모리에 올라간다.→ RAM 의 용량이 클 수록, 동시에 실행시킬 수 있는 앱의 총량이 높아진다고 생각할 수 있다.
    • → 그렇기 때문에 메모리에 저장된 데이터는 앱이 메모리에서 내려올 때 같이 내려오게 되는 것.
    • → 앱 실행중에 메모리에 저장된 데이터들은 앱을 종료하면 함께 삭제된다. (휘발된다)
    • 디스크보다 속도가 빠르다. (CPU 가 디스크보다 메모리에 더 빨리 접근할 수 있다.)
    • 디스크에 비해 용량이 작다. (보통 8GB, 16GB, 32GB)
    • EEPROM 과 같은 비휘발성 메모리도 있다. 아이폰은 이곳에 장치의 일련번호 및 하드웨어 정보를 저장한다. 

    디스크

    • 영구적인 데이터를 저장하는 곳. 비휘발성 장치.
    • → 앱 실행중에 디스크에 저장된 데이터들은 앱을 종료해도 디스크에 남는다.
    • 파일, 문서, 프로그램 등 상대적으로 용량이 큰 정보들을 담을 수 있다.
    • 메모리에 비해 속도가 느리다.
    • UserDefaults, CoreData 를 활용해서 디스크에 데이터를 저장할 수 있다.

    다음 주어진 상황들에서, 메모리를 활용하는게 좋을지 디스크를 활용하는게 좋을지 생각해봅시다. 정답은 드래그

    1. 전화번호부 앱에서 친구의 이름, 전화번호 정보 데이터. 디스크(실행마다 날아간다고 생각해봐라)
    2. 카운터 앱 개발할 때 사용했던 number 변수. 메모리(실행해서 초기부터 해도 되니까 굳이?)
    3. 스테이지가 있는 게임 앱에서 유저가 몇 스테이지까지 클리어했는지 정보. 디스크(마지막 스테이지에서 날아갔다고 생각해봐라)
    4. 유튜브나 인스타그램 같은 SNS 앱에서 추천 창에 뜬 이미지와 동영상 정보들. 메모리(이미 추천한 이미지가 미저리처럼 따라다닌다고?)

     

     

    class MyClass {
        //생성자 선언
        init() {
            print("My class 생성")
        }
        //소멸자 선언
        deinit {
            print("My class 소멸")
        }
    }
    
    //인스턴스 생성(인스턴스가 메모리를 할당 받음) => myClass에 대한 Reference Count = 1
    var myClass: MyClass? = MyClass()
    
    //myClass2가 myClass를 참조하게 된다. => Reference Count = 2
    var myClass2 = myClass
    
    // Reference Count = 2-1 = 1 => Reference Count > 0 메모리에 살아 있음
    myClass = nil
    // Reference Count = 1-1 = 0 => 메모리상에서 myClass가 사라져서 deinit이 호출된다. 
    myClass2 = nil
    class Jean {
        let mbti = "ENTP"
        init() {
            print("클래스 생성")
        }
        deinit {
            print("클래스 소멸")
        }
    }
    
    //옵셔널 타입의 Jean 인스턴스 생성 RC = 1
    var jean: Jean? = Jean()
    
    //아무런 인자도 리턴값도 없는 클로저 생성, jean을 참조했기 때문에 RC = 2
    let printMbti: () -> () = { [jean] in
        guard let jean else { return } //옵셔널을 해제해줄 guard let
        print("Jean's mbti = \(jean.mbti)")
    }
    
    printMbti()
    
    //메모리 할당 해제 -> printMbti에서 jean을 참조해서 남은 RC값이 1 남아 있어서 생성자 소멸 안 됨
    jean = nil
    // jean 참조시 weak를 넣어서 참조해야 한다. 아래 코드 참고.
     
    
    class Jean {
        let mbti = "ENTP"
        init() {
            print("클래스 생성")
        }
        deinit {
            print("클래스 소멸")
        }
    }
    
    //옵셔널 타입의 Jean 인스턴스 생성 RC = 1
    var jean: Jean? = Jean()
    
    //아무런 인자도 리턴값도 없는 클로저 생성, jean을 참조했기 때문에 RC = 2
    let printMbti: () -> () = { [ weak jean ] in
        guard let jean else { return } //옵셔널을 해제해줄 guard let
        print("Jean's mbti = \(jean.mbti)")
    }
    
    printMbti()
    
    //메모리 할당 해제 -> printMbti에서 jean을 참조해서 남은 RC값이 1 남아 있어서 생성자 소멸 안 됨
    jean = nil​
    class Person {
        var pet: Dog?
        init() {
            print("Person 클래스 생성")
        }
        deinit {
            print("Person 클래스 소멸")
        }
    }
    
    class Dog {
        var owner: Person?
        init() {
            print("Dog 클래스 생성")
        }
        deinit {
            print("Dog  클래스 소멸")
        }
    }
    
    // person RC = 1
    var person: Person? = Person()
    // dog RC = 1
    var dog: Dog? = Dog()
    // dog RC = 2
    person?.pet = dog
    // person RC = 2
    dog?.owner = person
    
    // person RC = 1
    person = nil
    // dog RC = 1
    dog = nil
    
    // lesson learned : 순환참조는 메모리 관리에 비효율적이기 때문에 피해야 한다. 순환참조가 발생하는 상황인지 점검할줄 알아야 한다.