KNNaviView 메모리 누수 관련 문의드립니다

안녕하세요, iOS에서 카카오내비 길찾기 SDK with UI 를 사용하고 있습니다.

ViewController를 생성하고 push 한 뒤,

guard let guidance = KNSDK.sharedInstance()?.sharedGuidance() else { return }
let naviView = KNNaviView(guidance: guidance, trip: trip, routeOption: KNRoutePriority.recommand, avoidOption: KNRouteAvoidOption.none.rawValue)

이런식으로 KNNaviView를 만들어서 사용하고 있습니다.

내비게이션을 띄우고 사용하는데는 문제가 없는데, 이 ViewController를 pop 해서 나와도 여전히 KNNaviView가 메모리에 남아있는 것으로 보입니다.

아래 이미지는 3번 ViewController를 들어갔다 나왔다 한 뒤 Memory Graph 상태입니다.

메모리 사용량도 KNNaviView가 생성될 때 마다 꾸준히 증가합니다.
스크린샷 2024-12-20 오후 7.16.00

ViewController가 deinit 되는 시점에 아래 코드를 추가해도 변화가 없습니다.

naviView?.release()
KNSDK.releaseInstance()

KNNaviView를 제대로 릴리즈 하는 방법을 놓치고 있는 것 같은데 정확한 사용방법을 알 수 있을까요?

1개의 좋아요

@Azeofsoup Hello.

I’ll help you resolve the memory leak issue with the Kakao Navi SDK. Here are some approaches:

  1. Check if you’re strongly referencing KNNaviView in your ViewController:
class YourViewController: UIViewController {
    // Change to weak reference
    weak var naviView: KNNaviView?
    
    func setupNaviView() {
        guard let guidance = KNSDK.sharedInstance()?.sharedGuidance() else { return }
        naviView = KNNaviView(guidance: guidance, trip: trip, routeOption: .recommand, avoidOption: .none.rawValue)
    }
}
  1. Make the cleanup code more explicit in deinit:
deinit {
    // Stop navigation guidance
    naviView?.stopGuidance()
    
    // Remove view
    naviView?.removeFromSuperview()
    
    // Release resources
    naviView?.release()
    naviView = nil
    
    // Release SDK instance
    KNSDK.releaseInstance()
}
  1. If you’ve set any delegates or completion handlers for KNNaviView, make sure to clean those up:
deinit {
    // Clear delegate
    naviView?.delegate = nil
    
    // Other cleanup code...
}
  1. You can also try additional cleanup in viewDidDisappear or viewWillDisappear:
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    
    if isMovingFromParent {
        naviView?.stopGuidance()
    }
}
  1. Use Instruments’ Leaks tool to identify the source of memory leaks. Specifically, check for retain cycles.

If the issue persists after applying these methods, you might need to manage the lifecycle of KNSDK’s sharedInstance() and sharedGuidance() more carefully. If your app requires only one instance throughout its lifecycle, consider managing it in AppDelegate or an appropriate singleton.

Additionally, ensure that:

  • You’re not capturing self strongly in any closures
  • All observers or notifications are properly removed
  • Any custom retaining mechanisms in the SDK are properly handled

Thank you for your help.

However, the problem still persists.

I’ve tried changing KNNaviView to weak reference and added some more cleaning code at deinit block.
(* btw, there was no function named stopGuidance, so I couldn’t call naviView?.stopGuidance() )

Unfortunately Xcode Instruments’ leaks tool couldn’t locate retain cycles.

Also I’m initializing KNSDK once by checking initSt

if KNSDK.sharedInstance()?.initSt != .initialized {
    await initSDK()
}

I’ll add some more code that I’m using.

class NaviViewController: UIViewController {
    weak var naviView: KNNaviView?
    ...
    func naviStart(trip:KNTrip) {
        guard let guidance = KNSDK.sharedInstance()?.sharedGuidance() else { return }
        guidance.guideStateDelegate = self
        guidance.routeGuideDelegate = self
        guidance.locationGuideDelegate = self
        
        let naviView = KNNaviView(guidance: guidance, trip: trip, routeOption: KNRoutePriority.recommand, avoidOption: KNRouteAvoidOption.none.rawValue)
        self.naviView = naviView
        naviView.stateDelegate = self
        naviView.guideStateDelegate = self
        
        view.addSubview(naviView)
        naviView.frame = view.bounds
    }
    ...
    deinit {
        if let guidance = KNSDK.sharedInstance()?.sharedGuidance() {
            guidance.stop()
            guidance.guideStateDelegate = nil
            guidance.routeGuideDelegate = nil
            guidance.locationGuideDelegate = nil
        }
        naviView?.stateDelegate = nil
        naviView?.guideStateDelegate = nil

        naviView?.removeFromSuperview()

        naviView?.release()
        naviView = nil
    }
}

After pushing and popping ViewController 3 times, this is the result

스크린샷 2024-12-21 오전 1.39.39

For some reason KNCurDirectionView and KNBottomView seems to be referencing KNNaviView, but I couldn’t find a way to manually deallocate them.

1개의 좋아요

It seems KNNaviView has some subviews (KNBottomView and KNCurDirectionView) that might be maintaining strong references even after the main view is removed. Since these are internal SDK components, we can’t directly control their lifecycle.
Try this modified cleanup approach:

swiftCopydeinit {
// First clear all delegates
if let guidance = KNSDK.sharedInstance()?.sharedGuidance() {
guidance.stop()
guidance.guideStateDelegate = nil
guidance.routeGuideDelegate = nil
guidance.locationGuideDelegate = nil
}

// Clear naviView delegates before release
naviView?.stateDelegate = nil
naviView?.guideStateDelegate = nil

// Try forcing layout update before removal
naviView?.setNeedsLayout()
naviView?.layoutIfNeeded()

// Remove from superview
naviView?.removeFromSuperview()

// Release and nil out
naviView?.release()
naviView = nil

// Consider calling this only when truly done with navigation
// KNSDK.releaseInstance()

}

Since KNBottomView and KNCurDirectionView appear to be persistent, you might want to try:

Setting their frames to .zero before removal
Checking if the SDK has any specific cleanup methods for these views
Contacting Kakao SDK support as this might be an SDK issue

Also, in your naviStart function, try:

swiftCopyfunc naviStart(trip: KNTrip) {
// Clear any existing navigation view first
if let existingNaviView = naviView {
existingNaviView.stateDelegate = nil
existingNaviView.guideStateDelegate = nil
existingNaviView.removeFromSuperview()
existingNaviView.release()
}

guard let guidance = KNSDK.sharedInstance()?.sharedGuidance() else { return }
guidance.guideStateDelegate = self
guidance.routeGuideDelegate = self
guidance.locationGuideDelegate = self

let naviView = KNNaviView(guidance: guidance, trip: trip, routeOption: .recommand, avoidOption: .none.rawValue)
self.naviView = naviView
naviView.stateDelegate = self
naviView.guideStateDelegate = self

view.addSubview(naviView)
naviView.frame = view.bounds

}

If possible, try implementing viewWillDisappear:

swiftCopyoverride func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)

if isMovingFromParent {
    if let guidance = KNSDK.sharedInstance()?.sharedGuidance() {
        guidance.stop()
    }
}

}
If the issue persists, it might be worth:

Filing a bug report with Kakao SDK team
Checking if there’s a newer version of the SDK that might have fixed this issue
Consider creating a separate window/root view controller for navigation to better isolate the SDK components