-3

As a follow up to this question: Skip/ignore method in iOS, I'm trying to implement separate delegates for my UITableView in iOS7 and iOS8.

So, as a first step, in viewDidLoad of my MyTableViewController, I added the following code:

if ([[[UIDevice currentDevice] systemVersion] compare: @"8.0" options: NSNumericSearch] != NSOrderedAscending)
{
    [self.tableView setDelegate: [[MyTVDelegate alloc] initWithIdentifier: myTVCellIdentifier]];
}
else
{
    [self.tableView setDelegate: [[MyTVDelegate7 alloc] initWithIdentifier: myTVCellIdentifier]];
}

I'm adding an identifier, since I will have to apply this to multiple view controllers (or I may have just create a delegate class for each TV, I haven't figured that out yet).

I'm using CoreData, so my dataSource is an NSFetchedResultsController.

Then, I have the following for MyTVDelegate/myTVDelegate7:

#import "MyTVDelegate.h"

@implementation MyTVDelegate

- (instancetype)initWithIdentifier: (NSString *) identifier
{
    if ([super init])
    {
        self.identifier = identifier;
    }

    return self;
}

@end

@implementation MyTVDelegate7

- (CGFloat)tableView: (UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath *)indexPath
{
    return 44;
}

- (CGFloat)tableView: (UITableView *)tableView estimatedHeightForRowAtIndexPath: (NSIndexPath *)indexPath
{
    return UITableViewAutomaticDimension;
}

@end

If I run this, I'm getting the following runtime error in iOS7:

2015-01-18 10:42:51.894  -[__NSArrayI tableView:estimatedHeightForRowAtIndexPath:]: unrecognized selector sent to instance 0x7b9dd220
2015-01-18 10:42:57.731  *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI tableView:estimatedHeightForRowAtIndexPath:]: unrecognized selector sent to instance 0x7b9dd220'

on iOS8, there is no crash.

The instance 0x7b9dd220 is an NSArray. My hunch is that it crashes, because the indexPath is invalid because the delegate and 'dataSource' are now separate?

I've tried moving the call to performFetch to either before or after setting the delegate, but I get the same error.

How do I fix this, should I for instance move all the NSFetchedResultsController code to the new delegate class as well?

Community
  • 1
  • 1
koen
  • 5,383
  • 7
  • 50
  • 89
  • This may be unrelated, but in your `initWithIdentifier` you need to assign `self` to the result of `[super init]`: `if (self = [super init])...` – pbasdf Jan 18 '15 at 16:35
  • Good catch! But as you said, it's unrelated to the issue. – koen Jan 18 '15 at 16:39
  • That error message implies that the tableView delegate is an instance of NSArray. Can you log `self.tableView.delegate` and see what class it is? – pbasdf Jan 18 '15 at 16:57
  • Looks ok to me: `po self.tableView.delegate ` Maybe I need to adjust something as well for my `fetchedResultsControler` ? – koen Jan 18 '15 at 17:02
  • I don't think the problem is linked to the FRC, but add the FRC code to your question, just in case. – pbasdf Jan 18 '15 at 17:12
  • Try turning on zombies in your build scheme and see if you get a useful message. To me, it looks as if your delegate has been deallocated and its address re-used for an array. – Phillip Mills Jan 18 '15 at 17:19
  • With zombies turned on, I get this: `[MyDelegate7 tableView:estimatedHeightForRowAtIndexPath:]: message sent to deallocated instance 0x7e806430 (lldb) po 0x7e806430 [MyDelegate7 respondsToSelector:]: message sent to deallocated instance 0x7e806430 2122343472` So, maybe theway I set the delegate for the TV is wrong? – koen Jan 18 '15 at 17:23
  • I don't think this will make any difference, but you don't need to implement either of those delegate methods anyway, and you're kind of doing it backwards . The rowHeight should be UITableViewAutomaticDimension (not the estimated height), and the estimatedRowHeight should be a fixed value. So, you can just use self.tableView.estimatedRowHeight = 44 and self.tableView.rowHeight = UITableViewAutomaticDimension instead of implementing the delegate methods. – rdelmar Jan 18 '15 at 17:37
  • 2
    Your real problem though, is that you're not retaining your delegate. You might need to create a strong property in MyTableViewController that points to your MyTVDelegate7 (or MyTVDelegate) instance. – rdelmar Jan 18 '15 at 17:39
  • @rdelmar: using a delegate (or actually two, one for iOS7 and iOS8) property in MyTVC solved the problem. – koen Jan 18 '15 at 18:46
  • @rdelmar: I think those values for `rowHeight` and `estimatedRowHeight` only work in iOS8 using the "self sizing cell" feature (see eg here: http://useyourloaf.com/blog/2014/08/07/self-sizing-table-view-cells.html). I still need to calculate them in iOS7, based on the contents of the cell. In my sample code above I used 44 and `UITableViewAutomaticDimension` as a placeholder for clarity. – koen Jan 19 '15 at 13:46

1 Answers1

1

self.tableView setDelegate: assigns a weak reference; if you don't hold your own reference to this object, it will get collected. This is why you're seeing the crash. The system has collected the memory that was assigned to your delegate, then reassigned the memory to an NSArray. Your table tries to call methods on the delegate and can't because NSArray does not respond to them.

Alongside the self.tableView property definition, define another property:

@property (strong) id<UITableViewDelegate> myTableViewDelegate;
Ian MacDonald
  • 13,472
  • 2
  • 30
  • 51
  • Based on the comment from @rdelmar above, I added this: `@property (nonatomic, strong) MyTVDelegate *delegate; @property (nonatomic, strong) MyTVDelegate7 *delegate7;`, so one for iOS7 and one for iOS8, which works fine. How is that solution different from yours? – koen Jan 19 '15 at 14:52
  • 1
    You really only need one delegate handle since you're only ever instantiating one of them. – Ian MacDonald Jan 19 '15 at 14:55
  • Excellent. I do have another follow up question, which I posted here: http://stackoverflow.com/questions/28101377/separate-delegate-for-uitableview-using-blocks-for-each-method – koen Jan 23 '15 at 00:54