It seems that objc_setAssociatedObject()
does not work with "tagged pointers".
Tagged pointers are special pointers that do not point to something, but the "value"
is stored in (some bits of) the pointer itself.
See for example Tagged pointers in Objective-C, which has links to more information.
Tagged pointers are used with 64-bit code only, e.g. for index paths with
small row and section numbers, or small number objects.
It can easily be verified that
setting an associated object on a tagged pointer crashes with EXC_BAD_ACCESS
on iOS 7/64-bit:
static void *key = &key;
id obj = [NSIndexPath indexPathForRow:1 inSection:1];
objc_setAssociatedObject(obj, key, @"345", OBJC_ASSOCIATION_RETAIN_NONATOMIC);
// --> EXC_BAD_ACCESS_(code=EXC_I386_GPFLT)
The same happens with
id obj = @1;
id obj = [NSDate dateWithTimeIntervalSinceReferenceDate:0];
which are also tagged pointers in 64-bit mode.
I could not find any documentation of this restriction and would consider this a
bug in iOS. A similar problem was reported here: https://github.com/EmbeddedSources/iAsync/issues/22. I think this is worth a bug
report to Apple.
(Remark: The problem does not occur on OS X/64-bit.)
Update (possible solution/workaround): The "SKSTableView" project (link at the end of the question) uses a category on
NSIndexPath
and associated objects to add a third property "subrow" to an index path.
This subrow is set temporarily in
tableView:cellForRowAtIndexPath:
of SKSTableView
to pass the current subrow to the
tableView:cellForSubRowAtIndexPath:indexPath
delegate method in the ViewController
class.
If you use "proper" three-level index paths instead, the
associated object is not needed and the sample app runs in 64-bit mode as well.
I made the following changes to the sample project:
In SKSTableView.m:
+ (NSIndexPath *)indexPathForSubRow:(NSInteger)subrow inRow:(NSInteger)row inSection:(NSInteger)section
{
// Change:
//NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
//indexPath.subRow = subrow;
// To:
NSUInteger indexes[] = { section, row, subrow };
NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:3];
return indexPath;
}
The property accessor methods
- (NSInteger)subRow;
- (void)setSubRow:(NSInteger)subRow;
are not needed anymore.
In ViewController.m:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForSubRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"UITableViewCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
// Change:
//cell.textLabel.text = [NSString stringWithFormat:@"%@", self.contents[indexPath.section][indexPath.row][indexPath.subRow]];
// To:
cell.textLabel.text = [NSString stringWithFormat:@"%@", self.contents[[indexPath indexAtPosition:0]][[indexPath indexAtPosition:1]][[indexPath indexAtPosition:2]]];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}