Sunday, June 15, 2014

Swift is Reference counted, NOT garbage collected

The first error I encountered was two fold, the difference between reference counting and garbage collection.  I was attempting to create a class to manage the data for a multi-level picker, and I dislike the standard practice Apple uses to have the view controller manage the pickers (I think it belongs in the subclass).

In my first attempt I tried the following code: (which would work great in Java)
 // Pass the callback to the new delegate, as well as the list of arrays.
        var delegate=PickerProvider(selectedCall:returnValue
            ,arrays:numDice,dieTypes,modRolls,modifiers);

        // Set the picker view to use the subclass as both a delgate and dataSource.
        pickerView.dataSource=delegate;
        pickerView.delegate=delegate;

When I did this, I would end up with a very obscure error:

014-06-15 21:16:11.737 SimpleDieRoller[11717:13412066] -[CALayerArray numberOfComponentsInPickerView:]: unrecognized selector sent to instance 0x10bf05330
2014-06-15 21:16:11.742 SimpleDieRoller[11717:13412066] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[CALayerArray numberOfComponentsInPickerView:]: unrecognized selector sent to instance 0x10bf05330'
*** First throw call stack:
(
 0   CoreFoundation                      0x000000010046ee35 __exceptionPreprocess + 165
 1   libobjc.A.dylib                     0x0000000101f1a9a0 objc_exception_throw + 45
 2   CoreFoundation                      0x00000001004751cd -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
 3   CoreFoundation                      0x00000001003cf89c ___forwarding___ + 988
 4   CoreFoundation                      0x00000001003cf438 _CF_forwarding_prep_0 + 120
 5   UIKit                               0x0000000100cd8398 -[UIPickerView _updateSelectedRows] + 69
 6   UIKit                               0x0000000100cd84ce -[UIPickerView didMoveToWindow] + 78
 7   UIKit                               0x0000000100d57052 -[UIView(Internal) _didMoveFromWindow:toWindow:] + 1496
 8   UIKit                               0x0000000100d56d41 -[UIView(Internal) _didMoveFromWindow:toWindow:] + 711
 9   UIKit                               0x0000000100d4f8e5 __45-[UIView(Hierarchy) _postMovedFromSuperview:]_block_invoke + 128
 10  UIKit                               0x0000000100d4f856 -[UIView(Hierarchy) _postMovedFromSuperview:] + 437
 11  UIKit                               0x0000000100d5968e -[UIView(Internal) _addSubview:positioned:relativeTo:] + 1610
 12  UIKit                               0x0000000100d29c3f -[UIWindow addRootViewControllerViewIfPossible] + 452
 13  UIKit                               0x0000000100d29e22 -[UIWindow _setHidden:forced:] + 276
 14  UIKit                               0x0000000100d369ad -[UIWindow makeKeyAndVisible] + 42
 15  UIKit                               0x0000000100ce4db9 -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 2753
 16  UIKit                               0x0000000100ce7529 -[UIApplication _runWithMainScene:transitionContext:completion:] + 1222
 17  UIKit                               0x0000000100ce6510 -[UIApplication workspaceDidEndTransaction:] + 19
 18  CoreFoundation                      0x00000001003a5bbc __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12
 19  CoreFoundation                      0x000000010039b095 __CFRunLoopDoBlocks + 341
 20  CoreFoundation                      0x000000010039a84c __CFRunLoopRun + 844
 21  CoreFoundation                      0x000000010039a296 CFRunLoopRunSpecific + 470
 22  UIKit                               0x0000000100ce5f26 -[UIApplication _run] + 413
 23  UIKit                               0x0000000100ce9308 UIApplicationMain + 2994
 24  SimpleDieRoller                     0x0000000100006c3d top_level_code + 77
 25  SimpleDieRoller                     0x0000000100006c7a main + 42
 26  libdyld.dylib                       0x0000000102478145 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException


It was not obvious from this what was happening.  What was really happening is that the 'var delegate' local variable was being allocated and given a count of 1.  It was then assigned to
'delegate' and datasource' of the picker.

However those properties are declared:
var dataSource: UIPickerViewDataSource! // default is nil. weak reference
    var delegate: UIPickerViewDelegate! // default is nil. weak reference

The fact that they were weak references, means that when the initializer left scope, the count was decremented, the variable was no longer valid, so the picker called an invalid reference.  

This was an easy fix, I just made a member variable that held my picker source, however the error was so obscure it took a while to find it out.



No comments:

Post a Comment