iosswiftnotificationswkwebviewlayer

Odd Crash in Swift, related to setting sublayers to nil


This is a follow on to this question. In this routine,

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    Tracker.track("getting row \(indexPath.row)")
    let ptv = tableView as? NovilloTableView
    if ptv!.uiType == .textTable {
        let gp = Projects.currentProject?.getPaths(type: PaletteView.getCurrentPane())
        GitPaths.currentGitPath = gp![indexPath.row]
//            NotificationCenter.default.post(name: NNames.updateWebText.nn(), object: nil)
        return
    }
    let svgs = Projects.currentProject!.getPaths(type : PaletteView.getCurrentPane())
    var gitPath = svgs[indexPath.row]
    Tracker.track("gitpath is \(gitPath)")
    var gitPaths = GitPaths.getMediaBoundingBoxes(paths: [gitPath])
    guard let pathArrays = gitPath.parseForRegBeziers() else { return }
    let rslt = pathArrays.0
    let regBeziers = pathArrays.1
    gitPath.boundingBox = gitPath.getBoundsParamsForPaths(src: regBeziers.isEmpty ? rslt : regBeziers)
    GitPaths.currentGitPath = gitPath
//        Tracker.track("sending notification")
    NotificationCenter.default.post(name: NNames.updateMedia.nn(), object: nil, userInfo: ["path" : gitPath])
    Tracker.track("completed didSelect")
    return
}

…the main thread logical path I'm following is the one that ends at the bottom withTracker.track("completed didSelect"). I'm getting a crash if I execute the notification call, that throws this information:

libobjc.A.dylib`objc_msgSend:
    0x18002ec00 <+0>:   cmp    x0, #0x0
    0x18002ec04 <+4>:   b.le   0x18002ec6c               ; <+108>
    0x18002ec08 <+8>:   ldr    x14, [x0]
    0x18002ec0c <+12>:  and    x16, x14, #0x7ffffffffffff8
    0x18002ec10 <+16>:  mov    x15, x16
->  0x18002ec14 <+20>:  ldr    x10, [x16, #0x10]
    0x18002ec18 <+24>:  lsr    x11, x10, #48
    0x18002ec1c <+28>:  and    x10, x10, #0xffffffffffff
    0x18002ec20 <+32>:  and    w12, w1, w11
    0x18002ec24 <+36>:  add    x13, x10, x12, lsl #4
    0x18002ec28 <+40>:  ldp    x17, x9, [x13], #-0x10
    0x18002ec2c <+44>:  cmp    x9, x1
    0x18002ec30 <+48>:  b.ne   0x18002ec3c               ; <+60>
    0x18002ec34 <+52>:  eor    x17, x17, x16
    0x18002ec38 <+56>:  br     x17
    0x18002ec3c <+60>:  cbz    x9, 0x18002eea0           ; _objc_msgSend_uncached
    0x18002ec40 <+64>:  cmp    x13, x10
    0x18002ec44 <+68>:  b.hs   0x18002ec28               ; <+40>
    0x18002ec48 <+72>:  add    x13, x10, w11, uxtw #4
    0x18002ec4c <+76>:  add    x12, x10, x12, lsl #4
    0x18002ec50 <+80>:  ldp    x17, x9, [x13], #-0x10
    0x18002ec54 <+84>:  cmp    x9, x1
    0x18002ec58 <+88>:  b.eq   0x18002ec34               ; <+52>
    0x18002ec5c <+92>:  cmp    x9, #0x0
    0x18002ec60 <+96>:  ccmp   x13, x12, #0x0, ne
    0x18002ec64 <+100>: b.hi   0x18002ec50               ; <+80>
    0x18002ec68 <+104>: b      0x18002eea0               ; _objc_msgSend_uncached
    0x18002ec6c <+108>: b.eq   0x18002ec90               ; <+144>
    0x18002ec70 <+112>: and    x10, x0, #0x7
    0x18002ec74 <+116>: asr    x11, x0, #55
    0x18002ec78 <+120>: cmp    x10, #0x7
    0x18002ec7c <+124>: csel   x12, x11, x10, eq
    0x18002ec80 <+128>: adrp   x10, 232550
    0x18002ec84 <+132>: add    x10, x10, #0xa00          ; objc_debug_taggedpointer_classes
    0x18002ec88 <+136>: ldr    x16, [x10, x12, lsl #3]
    0x18002ec8c <+140>: b      0x18002ec10               ; <+16>
    0x18002ec90 <+144>: mov    x1, #0x0
    0x18002ec94 <+148>: movi   d0, #0000000000000000
    0x18002ec98 <+152>: movi   d1, #0000000000000000
    0x18002ec9c <+156>: movi   d2, #0000000000000000
    0x18002eca0 <+160>: movi   d3, #0000000000000000
    0x18002eca4 <+164>: ret    
    0x18002eca8 <+168>: nop    
    0x18002ecac <+172>: nop    
    0x18002ecb0 <+176>: nop    
    0x18002ecb4 <+180>: nop    
    0x18002ecb8 <+184>: nop    
    0x18002ecbc <+188>: nop   

According to another post in Stackoverflow, that message has come up when functions that need to be visible to Objective-C aren't marked with @objc, but as you can see, this one is (below).

This wasn't happening at first, and I'm not sure why, but the function called by the Notification is this:

@objc func updateMedia(notification : Notification) {
    let path = (notification.userInfo?["path"] ??  GitPaths.currentGitPath!) as? GitPaths
    Tracker.track("sublayers: \(mediaDisplay!.layer.sublayers == nil)")
    mediaDisplay!.layer.sublayers = nil
    mediaDisplay!.mask = nil
//        Tracker.track("render beziers for \(path)")
//        path!.renderBeziers(tgt: mediaDisplay!, path : path) //, data:["style" : "media"])
//        refreshMediaInfo()
//        updateSelectedMedia( src : GitPaths.currentGitPath! )
//        return
}

I've commented most lines out to see where the crash can be induced, and it's the line mediaDisplay!.layer.sublayers = nil. If I comment this line out, the function executes correctly; if I include it, it will crash, but not as that line executes; the whole function will return, and the crash happens at the end of the function that called the Notification in the first place, which is the one at the top of this post. Tracker.track() is just a way to print messages in a formatted way, and isn't a contributor to this; so basically, after the Notification returns, nothing else happens; if I step through, it gets to the final bracket of the function, before returning control the the user.

I've checked that the object mediaDisplay exists, and it does because it's actually doing what is being asked; when not commented out, the line path!.renderBeziers(tgt: mediaDisplay!, path : path) , uncommented is drawing a bunch of Bezier paths into that view, which as this screenshot taken after the crash shows it does successfully. In other words, the line that causes the crash doesn't stop all the other code that's behind path!.renderBeziers(tgt: mediaDisplay!, path : path) from doing its job, when I uncomment those and run the same thing. The table in the palette is the object that initiates all this, btw.

enter image description here

The view has a big question mark hanging over it; it is a subclass of a WKWebView, which is the big change here. I'm using it here as for regular UIView capability, of acting as a container for a bunch of CAShapeLayers. This is working exactly as it was before, when it was a UIView.

The reason for the change is that I want to be able to display html content in the same view as the CAShapeLayers, as a way of having html interleaved between different drawn elements on the screen; think of text with the dark purple shape behind it and the lighter one in front. In this, I'm following a question I asked which was answered here.

In any case, referring to the container in the next line, where the mask is set to nil does not cause the crash; so it seems to have to do with the layer of the WKWebView, and the sublayers of it. They exist, and I've checked that, but setting them to nil seems to blow this up, in this weird way.

I'm sure I'm missing something; I haven't used WebViews before, so I'm expecting that maybe that's the issue; but it's not intuitive to me what could be going wrong, and I've tried multiple strategies for debugging this. The one that has gotten me the closest to pinpointing the problem is what I've shown here, where I can locate it in the one line; but it seems pretty unproblematic to me...am I missing something obvious?


Solution

  • It would appear that @Larme was on the right track: I eventually traced it to the line where the view's layer's sublayers were set to nil. This was the problem. Iterating through the sublayers if present and removing them individually from the parent layer caused the crash to disappear.

    The same problem cropped up in a second view, also a WKWebView, where applying the same solution caused a similar crash. In both cases, the error message was entirely unhelpful. In the second case, I simply commented out all the code related to sublayer, and things worked fine. I suspect that this might cause problems at a later stage when I need to update the view with other sublayer information, but I am not in a situation to test that right now.

    I'm travelling right now without access to my original project, so sorry for no code to show; but the basic iteration through the layer's sublayers should not be too hard to work out.