๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Swift

[Swift] MEMORY LEAK ์žก๊ธฐ

by yangsubinn 2023. 2. 17.

๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜, ๋ฉ”๋ชจ๋ฆฌ ๋ฆญ.. ์กฐ์‹ฌํ•ด์•ผ๋œ๋‹ค…. ์•ฝํ•œ ์ฐธ์กฐ ํ•ด์ค˜์•ผ๋œ๋‹ค … ์ž˜ ์•ˆํ•ด์ฃผ๋ฉด ์•ฑ ํ„ฐ์งˆ ์ˆ˜๋„ ์žˆ๋‹ค…

๋ผ๋Š” ๊ฒฝ๊ณ ๋ฅผ ๋“ฃ๊ธฐ๋งŒ ํ–ˆ์ง€, ์ง์ ‘ ๊ฒช์–ด๋ณด๊ธด ์ฒ˜์Œ์ด์—ˆ์Šต๋‹ˆ๋‹ค ..

 

์ตœ๊ทผ ์˜์ƒ์„ ๋‹ค๋ฃจ๋Š” ์„œ๋น„์Šค๋ฅผ ๊ฐœ๋ฐœํ•˜๊ณ  ์žˆ์—ˆ๋Š”๋ฐ์—ฌ

์„œ๋น„์Šค ๋‚ด์—์„œ ๋ผ์ด๋ธŒ ์˜์ƒ๋„ ๋‹ค๋ฃจ๊ณ , ๋‹ค์‹œ๋ณด๊ธฐ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ์˜์ƒ์„ ์ฒ˜๋ฆฌํ•˜๋‹ค๋ณด๋‹ˆ

์˜์ƒ์„ ๋Œ๋ฆฌ๋‹ค๋ณด๋ฉด ๋ฉ”๋ชจ๋ฆฌ ๋ฌธ์ œ๋กœ ์ธํ•ด์„œ ์•ฑ์ด ํ„ฐ์ง€๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ˜ก

ํŠนํžˆ ๋ผ์ด๋ธŒ ๋‹ค์‹œ๋ณด๊ธฐ ์˜์ƒ๊ฐ™์€ ๊ฒฝ์šฐ, 3์‹œ๊ฐ„์งœ๋ฆฌ ์˜์ƒ์„ ์ „๋‹ฌ๋ฐ›๋‹ค๋ณด๋‹ˆ

์˜์ƒ์„ ํ”Œ๋ ˆ์ดํ•  ๋•Œ๋งˆ๋‹ค ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํŒํŒ ์น˜์†Ÿ๊ณ ,, ์ตœ๋Œ€ 3๋ฒˆ๊นŒ์ง€ ํ”Œ๋ ˆ์ดํ•˜๋ฉด ์•ฑ์ด ํ„ฐ์ง€๋”๋ผ๊ตฌ์š”..

์˜์ƒ์„ ํ•œ๋ฒˆ.. ๋‘๋ฒˆ.. ์„ธ๋ฒˆ.. ํ”Œ๋ ˆ์ดํ–ˆ์„๋•Œ..

์ €ํฌ๊ฐ€ ์ „๋‹ฌ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ์˜์ƒ์˜ ์ข…๋ฅ˜๋‚˜ ํฌ๊ธฐ๋Š” ์ผ๋‹จ ๋‹น์žฅ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์˜์—ญ์ด ์•„๋‹ˆ๋ผ ์ƒ๊ฐ๋˜์–ด

์ตœ๋Œ€ํ•œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ์žก์•„๋ณด์ž ํ•˜๋Š” ์ƒ๊ฐ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ•ด์ œํ•ด๋ณด๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.

 


๐Ÿš’ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ

* RxSwift + MVVM + Clean Architecture๋ฅผ ์ฑ„ํƒํ•œ ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค.

 

1. Closures

1) Subscribe

rxswift๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ตฌ๋…ํ•˜๋Š” ๋ชจ๋“  ๋ถ€๋ถ„์— ์•ฝํ•œ ์ฐธ์กฐ๋ฅผ ํ†ตํ•ด์„œ ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

onNext๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” withUnretained๋ฅผ ์‚ฌ์šฉํ–ˆ๊ณ ,

์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด onError๊นŒ์ง€ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” .subscribe(with:)์„ ์‚ฌ์šฉํ•ด์„œ ํ•œ๋ฒˆ์— ์•ฝํ•œ ์ฐธ์กฐ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

// ex1
enterButton.rx.tap
            .withUnretained(self)
            .subscribe { owner, _ in
                owner.dismiss(animated: true) {
                    let userInfo = ["matchId": owner.matchId,
                                    "matchInfo": owner.viewModel.matchInfoModel as Any,
                                    "matchScore": owner.viewModel.matchScoreModel as Any]
                    owner.postObserverAction(.presentPlayVC,
                                             userInfo: userInfo)
                }
            }.disposed(by: self.disposeBag)

//ex2
output.hasDuplicatedUser
            .withUnretained(self)
            .subscribe { owner, hasUser in
                if hasUser {
                    owner.showToast(message: I18N.Play.alreadyPlaying, bottom: 100)
                    owner.renewPlayerList(reload: true)
                }
            }.disposed(by: self.disposeBag)

// ex3
matchRepository.getMatchInfo(matchId: matchId)
            .subscribe(with: self) { (owner, entity) in
                guard let entity = entity else { return }
                let model = entity.toDomain()
                owner.matchInfo.accept(model)
            } onError: { owner, error in
                owner.error.accept(error)
            }
            .disposed(by: disposeBag)

 

2) UIView.animate

animations๋„ @escaping closure์ด๊ธฐ ๋•Œ๋ฌธ์— ์ „๋ถ€ ์•ฝํ•œ ์ฐธ์กฐ ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

UIView.animate๋Š” ์ˆœํ™˜์ฐธ์กฐ๊ฐ€ ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.. ! ์•Œ๋ ค์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.. 

์ฐธ๊ณ  https://daeun28.github.io/%EC%9D%B4%EB%A1%A0/post29/

 

3) DiffableDataSource (cellProvider)

UITableViewDiffableDataSource๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ,

์ด๋•Œ cellProvider๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ @escaping closure์ด๊ธฐ ๋•Œ๋ฌธ์— ์•ฝํ•œ ์ฐธ์กฐ๋ฅผ ํ•ด์คฌ์Šต๋‹ˆ๋‹ค.

UITableViewDiffableDataSource<TeamType, PlayerModel>(tableView: self.tableView, cellProvider: { [weak self] (tableView, indexPath, _) in
            guard let self = self else { return UITableViewCell() }
            guard let cell = tableView.dequeueReusableCell(withIdentifier: AddPlayerTVC.className, for: indexPath) as? AddPlayerTVC else { return UITableViewCell() }
            switch indexPath.section {
            case 0:
                let data = self.homePlayerList[indexPath.row]
                let isMe: Bool = data.userId == UserDefaults.standard.integer(forKey: Const.UserDefaultsKey.userId)
                cell.setData(data: data, isMe: isMe)
                if self.duplicatedUserChecked && data.isPlaying {
                    cell.setDuplicatedUI()
                }
						...
            return cell
        })

 

2. Custom UIView

ํ”Œ์  ๋‚ด์—์„œ ๋„ค๋น„๋ฐ”๋ฅผ CustomNavigationBar์ด๋ผ๋Š” UIView๋กœ ์ปค์Šคํ…€ํ•ด์„œ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

์™ผ์ชฝ์˜ backButton์„ ๋ˆŒ๋ €์„๋•Œ ๊ฐ ๋ทฐ์ปจ์—์„œ ๋ณ„๋‹ค๋ฅธ ์ง€์ •์„ ํ•ด์ฃผ์ง€ ์•Š์•„๋„ ํ•ญ์ƒ pop๋˜๋„๋ก ํ•˜๋Š” ์•ก์…˜์„ ๋„ฃ์–ด์ฃผ๊ธฐ ์œ„ํ•ด

CustomNavigationBar ์•ˆ์— pvc๋ผ๋Š” UIViewController์„ ๊ฐ–๊ณ  ์‚ฌ์šฉํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

class CustomNavigationBar: UIView {
    
    private var pvc: UIViewController?

		...
}

// ๋ทฐ์ปจ ๋‚ด์—์„œ ์•„๋ž˜์ฒ˜๋Ÿผ self(UIViewController)์„ ๋„ฃ์–ด์ค˜์„œ ์‚ฌ์šฉ
naviBar.setNavibar(self, title: I18N.Play.startPlay, type: .withCloseButton)

๋ทฐ ๋‚ด์˜ UIView๊ฐ€ UIViewController๋ฅผ ์ฐธ์กฐํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ˆœํ™˜ ์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด์„œ ์ฐธ์กฐ ํ•ด์ œ๊ฐ€ ์•ˆ๋ฉ๋‹ˆ๋‹ค..

์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด์„œ UIView ๋‚ด์—์„œ UIViewController๋ฅผ ์•ฝํ•œ ์ฐธ์กฐ๋กœ ์„ ์–ธํ•ด์ฃผ๋ฉด

class CustomNavigationBar: UIView {
    
    private weak var pvc: UIViewController?

		...
}

customNavigationBar์ด๋ผ๋Š” UIView๋ฅผ ๊ฐ–๋Š” ์ƒ์œ„ UIViewController๊ฐ€ deinit๋˜๋ฉด

ํ•˜์œ„์˜ UIView๋„ deinit๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค..

 

 

3. Protocol

delegate pattern์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋„ ํ”„๋กœํ† ์ฝœ์„ ์„ ์–ธํ• ๋•Œ weak๋กœ ์„ ์–ธํ•ด์ค˜์•ผ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œ๋ฉ๋‹ˆ๋‹ค..!

class AddPlayerFooterView: UITableViewHeaderFooterView {
    
    weak var buttonDelegate: AddPlayerDelegate?
		...
}

 

 

ํ‹€๋ฆฐ ๋ถ€๋ถ„์ด๋‚˜ ๋ณด์ถฉํ•˜๋ฉด ์ข‹์„ ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด ์–ธ์ œ๋“  .. ๋ง์”€ํ•ด์ฃผ์‹ญ์…” .. ๐Ÿ™‡‍โ™€๏ธ

'Swift' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[RxSwift] ์—๋Ÿฌ ํ•ธ๋“ค๋ง  (2) 2023.02.09
[Swift] RxSwift์—์„œ Combine์œผ๋กœ ๋ฐ”๊ฟ”๋ณด๊ธฐ  (2) 2022.09.26
[Swift] Array ๋ฐฐ์—ด  (0) 2022.06.11