0

One of my old question had to do with viewing pdf files in monotouch ( I managed to accomplish this). Port of the iOS pdf viewer for xamarin

My issue is as following: if I start to close and open a pdf view( view with catiledlayer) really fast and often my app crashes with a:

Got a SIGSEGV while executing native code. This usually indicates a fatal error in the mono runtime or one of the native libraries used by your application.

After researching around the internet for a few days I found a post saying something along the lines of: The image back store is being cleaned and this is causing the error.


Edit: Ok, I have come to the conclusion that my app is cleaning the memory and my pointers are turning into nulls. I called Gc.Collect() a couple of times and this seems to be the root of the problem.

I have removed all my calls to GC.Collect() and I currently running a stress test and will update as I identify the issue.

After running some more tests this is what I found out:

  • The error seems to orignate from the TiledLayerDelegate : CALayerDelegate class.

  • The app only crashes if the method Dispose from CALayerDelegate is called, overriding the method as empty seems to prevent the app from crashing.

  • Running the app seems to cause no issue whatsoever anymore. It is apparent that something is going really wrong on the Dispose method of the CALayerDelegate.

  • Last finding: Running the app like a monkey tends to heat up the app a good bit. I assume this is due to the intensive rendering of pdf pages ( they are huge sheets about 4,000 X 3,000 pxs)

    protected override void Dispose (bool disposing)
    {
      try{
          view = null;
          GC.Collect (2);
          //base.Dispose (disposing);
      }catch(Exception e) {
        //System.Console.Write(e);
      }
    }
    

Now more than anything, I am just wondering if the phone heating up is really as I assume nothing more than the CPU rendering the sheets and is normal. Does anyone have any ideas as to how best deal with the Dispose override?

Last Edit: for anyone wanting to prevents crashes this is what my last version of the layer view class looks like.

public class TiledPdfView : UIView {
    CATiledLayer tiledLayer;

    public TiledPdfView (CGRect frame, float scale)
        : base (frame)
    {
        tiledLayer = Layer as CATiledLayer;
        tiledLayer.LevelsOfDetail = 4; //4
        tiledLayer.LevelsOfDetailBias = 4;//4
        tiledLayer.TileSize = new CGSize (1024, 1024);
        // here we still need to implement the delegate
        tiledLayer.Delegate = new TiledLayerDelegate (this);
        Scale = scale;

    }

    public CGPDFPage Page { get; set; }

    public float Scale { get; set; }


    public override void Draw (CGRect rect)
    {
        // empty (on purpose so the delegate will draw)
    }


    [Export ("layerClass")]
    public static Class LayerClass ()
    {
        // instruct that we want a CATileLayer (not the default CALayer) for the Layer property
        return new Class (typeof (CATiledLayer));
    }

    protected override void Dispose (bool disposing)
    {
        Cleanup ();
        base.Dispose (disposing);
    }

    private void Cleanup ()
    {
        InvokeOnMainThread (() => {
            tiledLayer.Delegate = null;
            this.RemoveFromSuperview ();
            this.tiledLayer.RemoveFromSuperLayer ();

        });
    }
ScarletMerlin
  • 465
  • 4
  • 24

1 Answers1

2

Apple's sample code around that is not really great. Looking at the source of your tiled view I do not see a place where you set the layer delegate to nil. Under the hood, CATiledLayer creates a queue to call the tiled rendering in the background. This can lead to races and one way to work around this is explicitly nilling the delegate. Experiments showed that this can sometimes block, so expect some performance degradation. Yes, this is a bug and you should file a feedback - I did so years ago.

I'm working on a commercial PDF SDK (and we have a pretty popular Xamarin wrapper) and we moved away from CATiledLayer years ago. It's a relatively simple solution but the nature of PDF is that to render a part, one has to traverse the whole render tree - it's not always easy to figure out what is on screen and what is not. Apple's renderer is doing an ok-ish job on that and performance is okay, but you'll get a better performance if you render into one image and then move that around/re-render as the user scrolls. (Of course, this is trickier and harder to get right with memory, especially on retina screens.)

If you don't have the time to move away from CATiledLayer, some people go with the nuclear option and also manually remove the layer from the view. See e.g. this question for more details.

Cœur
  • 37,241
  • 25
  • 195
  • 267
steipete
  • 7,581
  • 5
  • 47
  • 81
  • That link to the similar question helped me a lot. I am still running tests, but it would seem to work great as of now. – ScarletMerlin Jun 07 '16 at 19:53
  • Thank you, I got it under control now. Debugging it was a pain, but is all good now. I always try to avoid commercial libraries as they tend to be really expensive on the long run. – ScarletMerlin Jun 08 '16 at 13:49
  • It all depends on your use case and the features you need. Maintaining code is also expensive in the long run, and then you need this little feature and that little feature and then there is this small bug and that crasher and in the end you spent more time and money on it than what licensing would be. And I'm not even talking about the opportunity cost - what could you have built in the time you built a very basic PDF Viewer? We see this story over and over. But I don't know your use case so maybe it's for a quick throwaway app and you really don't need text selection or search. – steipete Jun 08 '16 at 21:12