9

我在我的项目中使用了一个名为SKSTableView的简洁表视图控制器,它允许每个表行有多个子行。此代码在32 位模式下完美运行,但当我在 iPhone 5S 或模拟器中以 4 英寸 64 位模式运行它时,当您点击一行以获取子行时,它会崩溃。我对 64 位和 32 位 iOS 系统的区别一无所知。我很想了解这里发生了什么。

您会注意到 *SubRowObjectKey 设置为 void - 我得到的错误是: EXC_BAD_ACCESS_(code=EXC_I386_GPFLT)
这是试图访问不存在的东西的一般保护错误(?)

当它崩溃时,Xcode 会突出显示这行代码:
objc_setAssociatedObject(self, SubRowObjectKey, subRowObj, OBJC_ASSOCIATION_ASSIGN);

Also there is this: Printing description of *(subRowObj): (id) [0] = Printing description of SubRowObjectKey:

It seems to be working great in 32bit mode but some how in 64bit it seems to loose where it is. Here below is the section of the code I am looking at.

#pragma mark - NSIndexPath (SKSTableView)
static void *SubRowObjectKey;
@implementation NSIndexPath (SKSTableView)
@dynamic subRow;
- (NSInteger)subRow
{
    id subRowObj = objc_getAssociatedObject(self, SubRowObjectKey);
    return [subRowObj integerValue];
}
- (void)setSubRow:(NSInteger)subRow
{
    if (IsEmpty(@(subRow))) {
        return;
    }
    id subRowObj = @(subRow);
    objc_setAssociatedObject(self, SubRowObjectKey, subRowObj, OBJC_ASSOCIATION_ASSIGN);
}
+ (NSIndexPath *)indexPathForSubRow:(NSInteger)subrow inRow:(NSInteger)row inSection:(NSInteger)section
{
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
    indexPath.subRow = subrow;   
    return indexPath;
}

您可以在此处下载并使用代码:https ://github.com/sakkaras/SKSTableView

4

3 回答 3

14

似乎这objc_setAssociatedObject()不适用于“标记指针”。标记指针是不指向某物的特殊指针,但“值”存储在指针本身的(某些位)中。例如,请参阅Objective-C 中的标记指针,其中包含指向更多信息的链接。

标记指针仅用于 64 位代码,例如用于具有小行号和节号或小号对象的索引路径。

可以很容易地验证在标记指针上设置关联对象会在 iOS 7/64 位上使用 EXC_BAD_ACCESS 崩溃:

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) 

同样的情况发生在

id obj = @1;
id obj = [NSDate dateWithTimeIntervalSinceReferenceDate:0];

它们也是 64 位模式下的标记指针。

我找不到有关此限制的任何文档,并认为这是 iOS 中的错误。这里报告了一个类似的问题:https ://github.com/EmbeddedSources/iAsync/issues/22 。我认为这值得向 Apple 报告错误。

(备注:OS X/64-bit 不会出现此问题。)


更新(可能的解决方案/解决方法): “SKSTableView”项目(问题末尾的链接)使用类别 NSIndexPath和关联对象将第三个属性“subrow”添加到索引路径。该子行临时设置在

tableView:cellForRowAtIndexPath:

ofSKSTableView将当前子行传递给

tableView:cellForSubRowAtIndexPath:indexPath

类中的委托方法ViewController

如果您改用“正确的”三级索引路径,则不需要关联的对象,并且示例应用程序也可以在 64 位模式下运行。

我对示例项目进行了以下更改:

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;
}

属性访问器方法

- (NSInteger)subRow;
- (void)setSubRow:(NSInteger)subRow;

不再需要。

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;
}
于 2014-02-04T20:19:39.290 回答
5

通过以下方法更改设置:

- (NSInteger)subRow;
- (void)setSubRow:(NSInteger)subRow;

- (NSInteger)subRow
{
    id myclass = [SKSTableView class];

    id subRowObj = objc_getAssociatedObject(myclass, SubRowObjectKey);
    return [subRowObj integerValue];
}

- (void)setSubRow:(NSInteger)subRow
{
    id subRowObj = [NSNumber numberWithInteger:subRow];
    id myclass = [SKSTableView class];
    objc_setAssociatedObject(myclass, nil, subRowObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

特别感谢 @tufnica 在https://github.com/sakkaras/SKSTableView/issues/2

于 2014-04-22T08:25:06.650 回答
0

我也遇到过这种情况,但在不同的情况下,我需要将属性添加到标记的指针类型。

这是我想出的;它利用了 NSMapTable weakToStrongObjectsMapTable。它甚至可以用作标记指针和常规对象类型的 objc_setAssociatedObject/objc_getAssociatedObject 的替代品:

static NSString* SomeVariableKey = @"SomeVariableKey";

@interface NSObject (SomeAddition)

- (void)setSomeVariable:(NSObject*)var {
    [[APObjectAssociationManager defaultManager] setValue:var forKey:SomeVariableKey forAssociatedObject:self];
}

- (NSObject*)someVariable {
    return [[APObjectAssociationManager defaultManager] valueForKey:SomeVariableKey forAssociatedObject:self];
}

@end


@interface APObjectAssociationManager ()

@property (nonatomic, strong) NSMapTable *associations;

@end

@implementation APObjectAssociationManager


- (id)init {
    self = [super init];

    if (self) {
        self.associations = [NSMapTable weakToStrongObjectsMapTable];
    }

    return self;
}

+ (APObjectAssociationManager*)defaultManager {
    static APObjectAssociationManager *instance = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[APObjectAssociationManager alloc] init];
    });

    return instance;
}

- (void)setValue:(id)value forKey:(NSString *)key forAssociatedObject:(NSObject*)associatedObject {


    NSMutableDictionary *dictionary = [self.associations objectForKey:associatedObject];

    if (!dictionary) {
        dictionary = [NSMutableDictionary dictionary];
        [self.associations setObject:dictionary forKey:associatedObject];
    }

    [dictionary setValue:value forKey:key];
}

- (id)valueForKey:(NSString *)key forAssociatedObject:(NSObject*)associatedObject {

    NSDictionary *dictionary = [self.associations objectForKey:associatedObject];
    return dictionary[key];
}

@end
于 2015-03-11T20:06:37.580 回答