1

I have a major issue that I am stuck with at work and would REALLY appreciate some help on. This has cost me 2 days already.

What I am trying to do is have a special image class fire it's assigned callback when it's touched. Thats it.

But when I touch the image it crashes, often without an error message just (lldb). Sometimes it says garbage like "[__NSCFData tapped:]: unrecognized selector sent to instance 0x1780af360". Sometimes it says "message sent to deallocated object".

I can run the app 10 times in a row and get one of these random messages just from tapping the same object on the screen each time.

The code is very simple:

//view controller
var k:K_PreviewImage!
override func viewDidLoad()
    {
        var image:iImage = iImage(imageName: "ja3.jpg")
        k = K_PreviewImage(image: image)
        k.touchCallback = nestedTap
        _view.addSubview(k.image)
    }

   func nestedTap(k:K_PreviewImage)
    {
        println("Successs")
    }

And here is the code inside K_PreviewImage (my clickable image). This does NOT inherit from anything, including NSObject

var touchCallback:((K_PreviewImage)->Void)
    {
        set{
            if(_touchCallback == nil)
            {
                var tap:UIGestureRecognizer = UITapGestureRecognizer(target: self, action:"tapped:")
                _image.addGestureRecognizer(tap)
            }

            _touchCallback = newValue
        }
        get{
            return _touchCallback
        }

  func tapped(tap:UITapGestureRecognizer)
    {
        println("here")
        if(_touchCallback != nil)
        {
            touchCallback(self)
        }
    }

The above code causes a crash 100% of the time.

If I add @objc too the tap event listener in the K_PreviewImage like this

 @objc func tapped(tap:UITapGestureRecognizer)
    {
        println("here")
        if(_touchCallback != nil)
        {
            touchCallback(self)
        }
    }

Then the code WORKS and the touch event is fired (both inside K_PreviewImage and the controllers callback function 'nestedTap'.

Not sure why that works, but it does. However I'm still up a creek without a paddle because when I make the K_PreviewImage a function variable instead of a class member variable, I get a crash again

 override func viewDidLoad()
    {
        var image:iImage = iImage(imageName: "ja3.jpg")
        var k:K_PreviewImage = K_PreviewImage(image: image)
        k.touchCallback = nestedTap
        _view.addSubview(k.image)
    }

So my first question is why did I need to add @objc to get the callback to fire instead of giving me an unclear 'deallocated' memory crash?

My second question is why is it when I moved the variable from a member variable to a function variable does it cause the crash all over again. If the object was deallocated it couldnt hear the tap event in the first place could it? Why would it still be on screen? And why is it getting dealloacted just because it's not a member variable!

How the heck can I create a custom object dynamically and have it fire click events!

UPDATE

Here is the full code block involving how my K_PreviewImage was being added with a touch event

var _array:Array<iImage>!

    override func viewDidLoad()
    {
        _array = Array<iImage>()
        var image:iImage = iImage(imageName: "ja3.jpg")
        var k:K_PreviewImage = K_PreviewImage(image: image)
        k.touchCallback = nestedTap
        _view.addSubview(k.image)
        _array.append(k.image)
    }

    func nestedTap(k:K_PreviewImage)
    {
        println("Successs")
    }

Solved:

override func viewDidLoad()
    {
        _array = Array<K_PreviewImage>()
        var image:iImage = iImage(imageName: "ja3.jpg")
        var k:K_PreviewImage = K_PreviewImage(image: image)
        k.touchCallback = nestedTap
        _view.addSubview(k.image)
        _array.append(k)
    }

    func nestedTap(k:K_PreviewImage)
    {
        println("Successs")
    }

Because I was not storing a reference to the K_PreviewImage 'k' even though the subview was added, even though it belongs to k, k was not being retained. By making the array store a reference to K_PreviewImage instead of iImage the compiler now retains a reference to the K_Preview Image

Aggressor
  • 13,323
  • 24
  • 103
  • 182

1 Answers1

3

First off, it looks like you're trying to add an image as a subview. I don't know Swift all that well, but _view.addSubview(k.image) looks wrong to me and would make more sense as _view.addSubview(k).

Secondly, you're not keeping your reference to your K_PreviewImage. You are assigning k.image as the subview, which causes k to be detected as "no longer used" by the system. It will clean it up and when you try to access it, you'll crash.

Ian MacDonald
  • 13,472
  • 2
  • 30
  • 51
  • First off thanks for taking the time to review this and give a thoughtful response. Regarding the system detecting K as 'no longer user' why is that? I am storing K inside of an array which is a member variable. By adding I.image why does the system remove the parent and keep the child? image is a member variable of K! It wasnt shown in this example, but I do store K in an array after adding it to the view with _array.append(K) – Aggressor Oct 17 '14 at 18:22
  • 1
    Can you update your source to reflect the correct scope of `k`? Because you're getting an error that states `[__NSCFData tapped:]` is trying to be accessed, it looks a lot like the object you're expecting is not the object you have -- indicating that the memory was released back to the system and an `NSData` object appeared in its place. – Ian MacDonald Oct 17 '14 at 18:26
  • Brilliant thanks to you I figured it out. I was storing the iImage class in the array reference, but that apparently does NOT retain the object that is a part of (i.e. the K_PreviewImage) but making the array and array of K_PreviewImages the variable is retained properly now. THANK YOU THANK YOU THANK YOU! I was stuck on this for days, and resorted to passing data in with a sub class UIGestureTapEvent! Now I can have clean code again!!!! – Aggressor Oct 17 '14 at 18:38