• 카테고리

    질문 & 답변
  • 세부 분야

    모바일 앱 개발

  • 해결 여부

    미해결

[65강] MemberListManager 질문있어요.

23.07.31 12:14 작성 23.07.31 12:17 수정 조회수 228

0

안녕하세요. MemberListManager 클래스를 ViewController에서 인스턴스를 생성하는 과정에서 의문점이 생겨 질문글을 남깁니다.

 

1️⃣

강의 영상에서는 별도 makeMembersList() 메소드를 불러서 홍길동, 임꺽정 등 인물 정보를 불러오고 있습니다.

 

하지만, makeMemersList() 메소드가 아닌 별도 이니셜라이저를 정의해 인스턴스를 생성할 때 인물 정보를 불러오도록 시도하면 인물 정보가 두 번 불러와서(초기화되서) 제일 마지막 조커의 ID가 6이 아닌 13이 되네요.

class ViewController: UIViewController {

    // 테이블 뷰
    private let tableView = UITableView()
    
    // MARK: - 관리 모델 선언
    
    // MVC패턴을 위한 데이터 매니저 (배열 관리 - 데이터)
    var membersListManager = MembersListManager()
    
    // 네비게이션 바에 넣기 위한 버튼
    lazy var plusButton: UIBarButtonItem = {
        let button = UIBarButtonItem(
            barButtonSystemItem: .add,
            target: self,
            action: #selector(plusButtonPressed)
        )
        return button
    }()
    
    // NOTE: - lazy 변수로 선언한 이유⭐️
    //       - plusButton을 일반적인 프로퍼티로 선언했다면, target으로 self를 사용할 때 초기화되지 않은 self를 참조하게 되어 오류가 발생할 수 있습니다.
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupNaviBar()
        setupTableView()
        setupTableViewConstraints()
        
        setupDatas()
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        // 뷰가 다시 나타날 때, 테이블 뷰 리로드
        tableView.reloadData()
    }
class MembersListManager {
    
    // 멤버리스트를 저장하기 위한 배열
    private var membersList: [Member] = []
    
    init() {
        membersList = [
            Member(name: "홍길동", age: 20, phone: "010-1111-2222", address: "서울"),
            Member(name: "임꺽정", age: 23, phone: "010-2222-3333", address: "서울"),
            Member(name: "스티브", age: 50, phone: "010-1234-1234", address: "미국"),
            Member(name: "쿡", age: 30, phone: "010-7777-7777", address: "캘리포니아"),
            Member(name: "베조스", age: 50, phone: "010-2222-7777", address: "하와이"),
            Member(name: "배트맨", age: 40, phone: "010-3333-1234", address: "고담씨티"),
            Member(name: "조커", age: 40, phone: "010-4321-1234", address: "고담씨티")
        ]
    }

    // <...이하 생략...>

아래는 인스턴스의 생성 과정을 추적하기 위해 콘솔에 멤버 리스트를 출력한 결과입니다.

0 - Optional("홍길동")

1 - Optional("임꺽정")

2 - Optional("스티브")

3 - Optional("쿡")

4 - Optional("베조스")

5 - Optional("배트맨")

6 - Optional("조커")

7 - Optional("홍길동")

8 - Optional("임꺽정")

9 - Optional("스티브")

10 - Optional("쿡")

11 - Optional("베조스")

12 - Optional("배트맨")

13 - Optional("조커")

테이블에는 6 ~ 13번까지 결과가 표시됩니다. ViewController에서는 분명 하나의 인스턴스만 생성했음에도, 왜 이런 결과가 출력되는 건가요?

 

2️⃣

그리고 ViewController에서 plusButton 변수를 선언할 때, lazy 변수로 선언하는 이유가 무엇인가요? 대충 ViewController의 초기화 과정과 연관(ViewController 인스턴스가 만들어지기도 전에 self를 할당하면 nil로 대입?)되어 있다는 점만 알고 있는데, 정리가 안되서 그러는데 설명 부탁드려도 될까요?

 

답변 2

·

답변을 작성해보세요.

0

지금 현재의 문제는..

MemberListManager의 init() 부분에.. print(#function) 해서 확인해보시면.. 쉽게 파악이 되시는데,

image

MemberListManager의 init( ) 생성자가.. 순간적으로 두번 호출이 되고 있어서 그렇습니다. MemberListManager가 2번 호출이 된다는 얘기는.. 다시 말하면.. MemberListManager가 2번 생성이 된다는 얘기죠.
(멤버리스트 매니저가 순간적으로 2번 생성이 되면서, 메모리상에 잠시나마 배열이 반복되어서, 보이는 현상처럼 보입니다. 나중 시점에 배열의 갯수를 확인해보면 다시 줄어들긴 하네요.)


그리고 MemberListManager가 2번 생성이 된다는 것은 다시 뷰컨트롤러가 2번 생성이 되고 있기 때문에 그렇습니다. 왜냐면..

image

MemberListManager가 생성되는 곳은 뷰컨트롤러 내부이기 때문이죠...



그래서, 이런 이유가 무엇일까 확인해보니..

(1) 현재 네비게이션 컨트롤러를 생성하면서 뷰컨트롤러를 생성하고 있고 (씬 델리게이트 부분)

image

(2) 스토리보드 파일도 남아있어서.. 그렇습니다. (내부 메커니즘상 스토리보드 파일이 불리면서 또 ViewController( ) 가 한번 더 호출이 됩니다.)

 

그래서 이 문제를 해결하시려면.. 어짜피 코드로 UI를 짰으니, 스토리보드를 아예 삭제하시면 문제는 해결됩니다.

스토리보드 완전 삭제 관련 참고 내용 링크:
https://declan.tistory.com/19

 

제가 확인해보니.. 스토리보드를 완전 삭제하니 문제가 잘 해결됩니다.

일반적으로는 코드로 UI를 짤때, 위의 링크처럼 스토리보드를 완전삭제를 하는 경우가 많으니 참고적으로 보시면 될 것 같아요. (제 강의에서는 그냥 설명의 편의를 위해서 스토리보드 파일은 삭제하지 않고 진행했더니 이런 문제가 생기나 봅니다.)


그리고
plusButton을 lazy var 로 생성하는 이유는,
image

내부 코드를 보시면.. 버튼 생성시에 target으로 self를 연결해주죠? self. 즉, 뷰컨트롤러를 연결해주고, 뷰컨트롤러가 가진 메서드(함수) - plusButtonPressed 메서드를 연결해주는데.. (그래야 버튼을 눌렀을때 해당 메서드가 호출이 되는데)

 

self. 즉, 뷰컨트롤러에 접근하려면.. 뷰컨트롤러가 이미 메모리에 올라가 있어야 합니다. (즉, 이미 생성이 되어 있어야 합니다.) 뷰컨트롤러가 생성이 되어있어야, 버튼을 만드는 시점에 이미 메모리에 올라간 뷰컨트롤러에 접근한 후에 연결을 시켜주는 것이죠.
(즉, 시점이 뷰컨트롤러 먼저 생성 ===> 버튼 생성의 순서가 필요합니다.)

 

그런데 여기서 plusButton을 lazy var가 아닌 그냥 var로 하시면.. 뷰컨트롤러 내부에 저장속성으로 선언한 plusButton의 생성시점이.. 뷰컨트롤러가 생성되는 동일시점에 생성이 됩니다. (클래스 내부의 저장속성의 생성 시점은 클래스가 생성되는 그 순간과 동일하겠죠.)

 

그래서, 뷰컨트롤러가 먼저 생성되어 있어야, 버튼을 생성하면서.. self(뷰컨트롤러)에 접근해서 연결을 시킬 수 있는 것입니다. (그래서 앞쪽에서도 로그인 화면 등을 만들면서도 지속적으로 (뷰컨트롤러보다) 생성 시점을 늦추기 위해서 lazy var로 선언 했던 것이고요. 예를 들어 addSubview나 addTarget 할려면, 이미 뷰컨트롤러가 메모리에 올라가 있어야 연결이 가능했던 것이죠. 이 부분들은 앞에 활용 3번째 앱 부분 자주하는 질문 확인 부탁드려요.)

 

위의 내용들을 잘 이해해보시길 바라고, 이해가 안되시는 부분이 있으면 다시 질문 주세요!

 

감사합니다. :)

0

건우님.
이 내용만 보고는 제가 알수 없습니다.
makeMembersList() 가 불릴 수 있는 코드가 있는지도 확인할 수도 없고요.

 

폴더를 압축해서 보내주시면 확인해드릴께요.
다만, 분명히 코드는 거짓말을 하지 않습니다. 어떤 부분에서 추가적으로 추가되는 부분이 있을꺼예요.

 

확인하기 어려우시다면, 전체 폴더를 압축해서
we.love.code.allen@gmail.com
으로 보내주세요!

 

감사합니다. :)

건우 김님의 프로필

건우 김

질문자

2023.07.31

네 지금 바로 보내드릴게요