Reputation: 322
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_ACCESS
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
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