-1

I'm working on an iOS app with collectionView in objective-c and I have an error.

Below code is for feeding each cell with data(image in this case). I pick up an image name from two dimensional array in Model class, and display it.

I made up a new array to prevent from duplication of image display by changing value in the array from '0' to '1' so next time can avoid display '1' marked image.

I succeeded initial setting all arrays with NSInteger 0, with replaceObjectAtIndex:withObject: method in a 'ViewDidLaod' method, however, I have an error when I want to change the value into '1' in the array with replaceObjectAtIndex:withObject:, the same method I used in the 'ViewDidLaod' method.

I already searched google and stack but people who got the same problem used immutable array, that's why they had an issue, but I have mutable array in my code. Please help me.

The error message is below:

2015-04-13 03:55:25.824 InfanTree[4474:166010] -[__NSCFNumber length]: unrecognized selector sent to instance 0xb000000000000002
2015-04-13 03:55:25.846 InfanTree[4474:166010] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFNumber length]: unrecognized selector sent to instance 0xb000000000000002'
*** First throw call stack:

(
    0   CoreFoundation                      0x000000010826fa75 __exceptionPreprocess + 165

    1   libobjc.A.dylib                     0x0000000107f08bb7 objc_exception_throw + 45

    2   CoreFoundation                      0x0000000108276d1d -[NSObject(NSObject) doesNotRecognizeSelector:] + 205

    3   CoreFoundation                      0x00000001081ce9dc ___forwarding___ + 988

    4   CoreFoundation                      0x00000001081ce578 _CF_forwarding_prep_0 + 120

    5   UIKit                               0x0000000108f13f06 +[_UIAssetManager createAssetNamed:fromBundle:] + 67

    6   UIKit                               0x00000001088d4f60 +[UIImage imageNamed:inBundle:compatibleWithTraitCollection:] + 221

    7   InfanTree                           0x00000001079c92d0 -[ViewController collectionView:cellForItemAtIndexPath:] + 512
    8   UIKit                               0x0000000108ed1fab -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:] + 244

    9   UIKit                               0x0000000108ed36e4 -[UICollectionView _updateVisibleCellsNow:] + 3445

    10  UIKit                               0x0000000108ed7391 -[UICollectionView layoutSubviews] + 243

    11  UIKit                               0x000000010891c1c3 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 521

    12  QuartzCore                          0x000000010cc5ac58 -[CALayer layoutSublayers] + 150

    13  QuartzCore                          0x000000010cc4f87e _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 380

    14  QuartzCore                          0x000000010cc4f6ee _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24

    15  QuartzCore                          0x000000010cbbd36e _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 242

    16  QuartzCore                          0x000000010cbbe482 _ZN2CA11Transaction6commitEv + 390

    17  UIKit                               0x00000001088a068d -[UIApplication _reportMainSceneUpdateFinished:] + 44

    18  UIKit                               0x00000001088a1375 -[UIApplication _runWithMainScene:transitionContext:completion:] + 2684

    19  UIKit                               0x000000010889fd35 -[UIApplication workspaceDidEndTransaction:] + 179

    20  FrontBoardServices                  0x000000010bd92243 __31-[FBSSerialQueue performAsync:]_block_invoke + 16

    21  CoreFoundation                      0x00000001081a4c7c __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12

    22  CoreFoundation                      0x000000010819a9c5 __CFRunLoopDoBlocks + 341

    23  CoreFoundation                      0x000000010819a183 __CFRunLoopRun + 851

    24  CoreFoundation                      0x0000000108199bc6 CFRunLoopRunSpecific + 470

    25  UIKit                               0x000000010889f7a2 -[UIApplication _run] + 413

    26  UIKit                               0x00000001088a2580 UIApplicationMain + 1282

    27  InfanTree                           0x00000001079d19d3 main + 115

    28  libdyld.dylib                       0x000000010aa60145 start + 1
)

libc++abi.dylib: terminating with uncaught exception of type NSException

Whole code is here

Below is part of my code.

@interface ViewController ()<UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
{
    DataModel *_data;
    // save the indexPath value itself of the picture which is selected at specific index path
    NSMutableArray *_displayedImageIndexPathHistory; 
    // save 1 for the first rendering so next time can avoid to be selected
    NSMutableArray *_isSelectedBefore; 
    // save 1 if rendered at least once so can avoid duplication
    NSMutableArray *_preventDuplication; 
}
@end

@implementation ViewController
.
.
.
- (void)viewDidLoad {
    [super viewDidLoad];
.
.
.
     // _data.imageSet is a two dimensional 'MUTABLE' array which has 'MUTABLE' arrays in it. 
     // Declared in a Model class. Each element has NSString *imageName.png.
    _preventDuplication = [_data.imageSet mutableCopy];
    _displayedImageIndexPathHistory = [_data.imageSet mutableCopy];
    _isSelectedBefore = [_data.imageSet mutableCopy];
    // set all arrays with 0
    NSInteger sectionNum = [_data.imageSet count];
    NSNumber *defaultNumber = [NSNumber numberWithInt:0];
    for (NSInteger i = 0; i < sectionNum; i++) {
        for (NSInteger j = 0; j < [_data.imageSet[i] count]; j++) {
            [_displayedImageIndexPathHistory[i] replaceObjectAtIndex:j withObject:defaultNumber];
            [_isSelectedBefore[i] replaceObjectAtIndex:j withObject:defaultNumber];
            [_preventDuplication[i] replaceObjectAtIndex:j withObject:defaultNumber];
        }
    }
.
.
.
}
.
.
.

-(UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    CollectionViewCell *cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    NSNumber *defaultNumber = [NSNumber numberWithInt:0];
    NSNumber *markingNumber = [NSNumber numberWithInt:1];

    if (indexPath.row == 0) {
        cell.cellImageView.image = [UIImage imageNamed:[_data.imageSet[indexPath.section] objectAtIndex:indexPath.row]];
        // **Have error at this sentence!!**
        [_preventDuplication[indexPath.section] replaceObjectAtIndex:indexPath.row withObject: markingNumber];
    }
    else {
        .
        .
        .
    }
    .
    .
return cell;
}

Thanks in advance.

Vincent Gigandet
  • 918
  • 10
  • 21

1 Answers1

2

Your code has the expression:

[UIImage imageNamed:[_data.imageSet[indexPath.section] objectAtIndex:indexPath.row]]

It would seem that this part:

[_data.imageSet[indexPath.section] objectAtIndex:indexPath.row]

Is producing an instance of NSNumber, not an instance of NSString. Of course, +[UIImage imageNamed:] requires its argument to be a string. It's apparently invoking -length on it. Since NSNumber doesn't respond to -length, you get that exception and crash.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • I used the same line at the previous version of my code and it worked. _data.imageSet array has NSStrings in it. [UIImage imageNamed:[_data.imageSet[indexPath.section] objectAtIndex:indexPath.row]] works. – Vincent Gigandet Apr 12 '15 at 20:01
  • Not according to the stack trace. It is showing the exception occurring in `+[UIImage imageNamed:...]`. Put a log line immediately above that line, like: `NSLog(@"%@ (%@)", [_data.imageSet[indexPath.section] objectAtIndex:indexPath.row], NSStringFromClass([[_data.imageSet[indexPath.section] objectAtIndex:indexPath.row] class]));` and you'll see. – Ken Thomases Apr 12 '15 at 20:09
  • Yes, you're right, it returns 0 (__NSCFNumber), not a string. But I don't understand why. It should be a string!, thanks anyways. – Vincent Gigandet Apr 12 '15 at 20:34
  • The loop in `-viewDidLoad` is putting `defaultNumber` (and `NSNumber`) at every element of the arrays. Perhaps your confusion is that you expect lines like `_preventDuplication = [_data.imageSet mutableCopy]` to make a **deep** copy, but it only makes a shallow copy. `_preventDuplication` is a separate mutable array from `_data.imageSet`, but its elements are the same elements as `_data.imageSet` has. So, `_preventDuplication[i]` is the same mutable array as `_data.imageSet[i]`. If you mutate `_preventDuplication[i]`, you mutate `_data.imageSet[i]`. – Ken Thomases Apr 12 '15 at 20:51