ios - 크래쉬 - IBOutlet이 EXC_BAD_ACCESS를 사용하여 충돌하지 않는 경우에도 충돌이 발생합니다.




아이폰 크래쉬 리포트 (4)

UIViewController (rolePageController)에서 다른 UIViewController (drawerController)를 구성하고 drawerController의 구성에 포함될 역할 페이지에서 2 UIViews를 전달합니다. drawerController가 rolePageController에서 IBOutlet 뷰에 액세스하려고하면 EXC_BAD_ACCESS (code = EXC_I386_GPFLT)와 충돌합니다.

첫 번째 VC (rolePageController)에는 다음과 같은 IBOutlets가 있습니다.

@IBOutlet var rolePageDrawerView: UIView!
@IBOutlet var rolePageContentView: UIView!

rolePageController.viewDidLoad ()에서 drawerController.configureDrawer (...)를 호출합니다.

override func viewDidLoad() {
    super.viewDidLoad()

    //other stuff happens here

    let drawerController = UIStoryboard(name: "StoryboardName", bundle: nil).instantiateViewController(withIdentifier: "drawerController") as! DrawerViewController
    drawerController.configureDrawer(drawerContainerView: self.rolePageDrawerView, overlaidView: self.rolePageContentView)

    //other stuff here
}

DrawerViewController 프로토콜은 다음과 같이 정의됩니다.

protocol DrawerViewController where Self: UIViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}

다음은 configureDrawer (...) func의 코드입니다.

private var drawerParentView: UIView!
private var overlaidByDrawerView: UIView!


func configureDrawer(drawerContainerView: UIView, overlaidView: UIView) {
    self.drawerParentView = drawerContainerView
    self.overlaidByDrawerView = overlaidView
}

디버거에서 호출 된 drawerController 인스턴스가 호출을 수신하는 자체 인스턴스와 일치하지 않는 것으로 나타났습니다. 다음은 호출 될 인스턴스의 주소입니다.

다음은 호출 할 때 인스턴스의 주소입니다.

호출하기 전에 drawerController의 주소가 self의 주소가 아닙니다. 그것은 결코 일어나서는 안됩니다.

나는 https://github.com/ksoftllc/DynamicStackBufferOverflow 에서 크래시를 재현하는 단순화 된 프로젝트를 만들었다.

솔루션 솔루션은 DrawerViewController 프로토콜에서 where 절을 제거하는 것으로 판명되었습니다.

protocol DrawerViewController where Self: UIViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}

문제가되는 코드를 찾았지만 왜 이것이 내가보고있는 오류를 일으키는 지 알지 못합니다. drawerController는 다음과 같이 정의 된 DrawerViewController 프로토콜을 따릅니다.

protocol DrawerViewController where Self: UIViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}

Where 조건을 제거하면 더 이상 충돌하지 않습니다.

protocol DrawerViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}

where 절은 프로그램의 올바른 기능을 수행하는 데 실제로 필요하지 않으므로이 절을 사용하지 않고 진행할 것입니다.

업데이트 swift.org에 버그를 제출하고 응답을 받았습니다. Swift 4.2에서는 프로토콜에 where 절을 추가 할 수 없지만 Swift 5.0에서는 지원됩니다. 또한 @J Doe는 Xcode 툴킷에 대한 업데이트로이를 수행하는 방법 아래에 게시했습니다.


문제를 해결하려면 개발 트렁크 툴 체인 스냅 샷에서 실행하십시오. 당신은 여기에서 그것을 다운로드 할 수 있습니다 :

https://swift.org/download/

스냅 샷 -> 트렁크 개발 (마스터) XCode (Swift 5.0이 아님)로 이동하여 12 월 15 일 스냅 샷을 다운로드하십시오. (11 월 30 일부터 스냅 샷을 얻었지만 12 월 15 일도 사용할 수 있습니다.)

툴체인을 설치 한 후 Xcode에서 File -> Preferences -> Components 하여 최신 툴체인을 선택하십시오. 이제 충돌없이 실행됩니다 .

또한 Self: UIViewController 를 단축 할 수있는 위치 :UIViewcontroller ( 최신 툴 체인에서만 작동 ) :

protocol DrawerViewController: UIViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}

정말 Swift 컴파일러 버그처럼 보입니다. 설명을 위해 코드를 단순화했습니다.

func foo(_ wow: TestProtocol) {
    wow.foo()
}

protocol TestProtocol where Self: NSObject {
    func foo()
}

class TestClass: NSObject, TestProtocol {

    func foo() {
        print("Much wow")
    }

}

foo(TestClass())

이것을 버그로보고 할 수 있습니다. 이 문제를 해결하기 위해 where 문이나 func foo(_ wow: TestClass { 형식의 객체를 전달하지 말 것을 제안합니다 func foo(_ wow: TestClass { .


dynamic-stack-buffer-overflow 는 재귀와 아무 관련이 없습니다. alloca 버퍼가 오버런되었음을 의미합니다. asan 런타임 소스 코드를 확인하십시오.

스택에 alloca 버퍼가 있고 그 뒤에 오브젝트 포인터가 오도록 (아마 인자로 전달 된 객체 포인터 중 하나조차도) 스택이 있다고 가정하십시오.

alloca 버퍼가 오버런된다고 가정하십시오. asan 빌드에서는 dynamic-stack-buffer-overflow 오류가 발생할 수 있습니다. 그러나 asan이 아닌 빌드에서는 객체 포인터의 바이트를 씁니다. 프로세스의 페이지 테이블에 매핑되지 않은 주소를 구성하는 바이트를 씁니다.

프로그램이 해당 객체 포인터를 읽고 다른 곳 (예 : 인스턴스 변수)에 저장하려고하면 객체의 참조 횟수를 증가시켜야합니다. 그러나 이는 포인터의 역 참조를 의미하며 포인터는 매핑되지 않은 주소를 가리 킵니다. 아마 Mach가 EXC_I386_GPFLT 호출하는 일반적인 보호 오류 EXC_I386_GPFLT 있습니다.

asan dynamic-stack-buffer-overflow 오류의 스택 추적을 게시하고 오류를 일으키는 코드를 디스 어셈블리 한 경우 유용합니다.





swift-protocols