lucas_turci
lucas_turci

Reputation: 322

Failed Trying to use Extensions - Swift

I've recently read The Swift Programming Language document, which introduced to me Extensions, and so I tried to implement this code:

extension SKTexture{
    var size: CGSize {
        return self.size()
    }
}

Later in the same code, I try to access a property of the SKTexture:

someTexture.size.width

However, when I run the app, I get a EXC_BAD_ACCESSenter image description here

I have also noticed that even if I don't try to access the width property via my new computed property, implementing someTexture.size().width instead of someTexture.size.width , I get this error. Could someone explain me what I'm doing wrong?

Upvotes: 2

Views: 408

Answers (1)

Martin R
Martin R

Reputation: 539955

Short answer:

For a class derived from NSObject, a Swift property which has the same name as an existing Objective-C method replaces that method.

Therefore in your case,

var size: CGSize {
    return self.size()
}

calls itself recursively until the program aborts with a stack overflow (well, that's what this site is for :).

If you choose a different name for the property, e.g.

var theSize: CGSize {
    return self.size()
}

then everything works nicely.


Long answer:

SKTexture is a subclass of NSObject. Therefore all Swift properties are "Objective-C compatible". As a consequence, the compiler generates a getter method that can be called from Objective-C code. The getter method for the size property is a -size method. So you have now two -size methods: The original one from SKTexture and a second one defined in your Swift code.

If you do the same with your own Objective-C class defined in the same project then you will get a linker warning:

instance method 'size' in category from /Users/.../main.o overrides method from class in /Users/.../MyClass.o

If the Objective-C class is defined in a external framework (as in your case) the linker does not notice the conflict.

Now return self.size() calls the generated Objective-C getter method, which in turn calls the extension method. This leads to "infinite" recursion and ultimately to a stack overflow.

This is confirmed by the stack backtrace which you can get with the lldb bt command when the program has crashed:

* thread #1: tid = 0x3d2ef, 0x000000010fb15e01 libobjc.A.dylib`objc::DenseMapBase, unsigned long, true, objc::DenseMapInfo > >, DisguisedPtr, unsigned long, objc::DenseMapInfo >, true>::FindAndConstruct(DisguisedPtr const&) + 21, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x7fff51b9cfe8)
    frame #0: 0x000000010fb15e01 libobjc.A.dylib`objc::DenseMapBase, unsigned long, true, objc::DenseMapInfo > >, DisguisedPtr, unsigned long, objc::DenseMapInfo >, true>::FindAndConstruct(DisguisedPtr const&) + 21
    frame #1: 0x000000010fb13e14 libobjc.A.dylib`objc_object::sidetable_retain() + 94
  * frame #2: 0x000000010d8674d9 cdtest2`ext.cdtest2.ObjectiveC.SKTexture.size.getter : C.CGSize(self=0x00007fcceea020f0) + 25 at AppDelegate.swift:19
    frame #3: 0x000000010d867542 cdtest2`@objc ext.cdtest2.ObjectiveC.SKTexture.size.getter : C.CGSize + 34 at AppDelegate.swift:0
    frame #4: 0x000000010d8674ed cdtest2`ext.cdtest2.ObjectiveC.SKTexture.size.getter : C.CGSize(self=0x00007fcceea020f0) + 45 at AppDelegate.swift:19
    frame #5: 0x000000010d867542 cdtest2`@objc ext.cdtest2.ObjectiveC.SKTexture.size.getter : C.CGSize + 34 at AppDelegate.swift:0
    frame #6: 0x000000010d8674ed cdtest2`ext.cdtest2.ObjectiveC.SKTexture.size.getter : C.CGSize(self=0x00007fcceea020f0) + 45 at AppDelegate.swift:19
    frame #7: 0x000000010d867542 cdtest2`@objc ext.cdtest2.ObjectiveC.SKTexture.size.getter : C.CGSize + 34 at AppDelegate.swift:0
    frame #8: 0x000000010d8674ed cdtest2`ext.cdtest2.ObjectiveC.SKTexture.size.getter : C.CGSize(self=0x00007fcceea020f0) + 45 at AppDelegate.swift:19
    ... 
    frame #149556: 0x000000010d8674ed cdtest2`ext.cdtest2.ObjectiveC.SKTexture.size.getter : C.CGSize(self=0x00007fcceea020f0) + 45 at AppDelegate.swift:19
    frame #149557: 0x000000010d867542 cdtest2`@objc ext.cdtest2.ObjectiveC.SKTexture.size.getter : C.CGSize + 34 at AppDelegate.swift:0
    frame #149558: 0x000000010d8694e0 cdtest2`cdtest2.AppDelegate.application (application=0x00007fccee8005a0, launchOptions=None, self=0x00007fccebc06410)(ObjectiveC.UIApplication, didFinishLaunchingWithOptions : Swift.Optional>) -> Swift.Bool + 112 at AppDelegate.swift:83
    frame #149559: 0x000000010d8697b0 cdtest2`@objc cdtest2.AppDelegate.application (cdtest2.AppDelegate)(ObjectiveC.UIApplication, didFinishLaunchingWithOptions : Swift.Optional>) -> Swift.Bool + 560 at AppDelegate.swift:0
    ...
    frame #149572: 0x000000010d86bcaa cdtest2`main + 42 at AppDelegate.swift:0
    frame #149573: 0x00000001102f0145 libdyld.dylib`start + 1

This (hopefully) explains also why the problem occurs with both someTexture.size().width and someTexture.size.width: In both cases, the custom extension method is called.

Upvotes: 4

Related Questions