【问题标题】:objc_setAssociatedObject function error in 64bit mode not in 32bit64 位模式下的 objc_setAssociatedObject 函数错误不在 32 位
【发布时间】:2014-02-28 22:29:44
【问题描述】:

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

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

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

还有这个: *(subRowObj) 的打印说明: (id) [0] = SubRowObjectKey 的打印说明:

它似乎在 32 位模式下工作得很好,但在 64 位模式下它似乎松散了。下面是我正在查看的代码部分。

#pragma mark - NSIndexPath (SKSTableView)
静态无效*SubRowObjectKey;
@implementation NSIndexPath (SKSTableView)
@动态子行;
- (NSInteger)subRow
{
    id subRowObj = objc_getAssociatedObject(self, SubRowObjectKey);
    返回 [subRowObj integerValue];
}
- (void)setSubRow:(NSInteger)subRow
{
    if (IsEmpty(@(subRow))) {
        返回;
    }
    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 = 子行;
    返回索引路径;
}

你可以在这里下载和玩代码:https://github.com/sakkaras/SKSTableView

【问题讨论】:

    标签: ios objective-c 64-bit 32-bit general-protection-fault


    【解决方案1】:

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

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

    很容易验证 在标记指针上设置关联对象会因 EXC_BAD_ACCESS 而崩溃 在 iOS 7/64 位上:

    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位不会出现此问题。)


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

    tableView:cellForRowAtIndexPath:
    

    of SKSTableView 将当前子行传递给

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

    【讨论】:

    • 您是否使用错误报告器输入错误报告?
    • @DavidH:还没有,我只是设法找出了 OP 项目崩溃的原因。
    • 即使 objc_setAssociatedObject() 确实适用于标记指针,它也不会做你想要的。在您的情况下,结果将是每个具有该路径值的 NSIndexPath 似乎都具有与之关联的相同 subRow 值。您将无法拥有 row=1,section=1,subrow=1 的 NSIndexPath 和 row=1,section=1,subrow=2 的另一个 NSIndexPath。
    • @GregParker:我还不明白在那个项目中 如何 使用 subrow 属性,但它似乎在 32 位模式下工作。 - 这是标记指针和 objc_setAssociatedObject() 的已知限制吗?
    • @MartinR 感谢您详细而清晰的回复。我将在我的项目中实施这些更改并重新测试应用程序。为了清楚起见,您可以将 Bug 输入 Apple。再次非常感谢!
    【解决方案2】:

    通过以下方法更改设置:

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

    特别感谢@tufnicahttps://github.com/sakkaras/SKSTableView/issues/2

    【讨论】:

    • 只有一个类对象[SKSTableView class]。如果您使用它来存储 subRow 编号,那么您还可以使用全局变量(没有任何关联对象)。
    【解决方案3】:

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

    这是我想出的;它利用了 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
    

    【讨论】:

    • 我喜欢这个想法,但是当使用标记为指针的对象时,弱键不会无用吗?我的印象是标记指针永远不会被视为已释放,因此 NSMapTable 一旦插入它们就永远不会释放值。
    猜你喜欢
    • 2012-07-02
    • 1970-01-01
    • 2014-05-16
    • 2016-07-13
    • 1970-01-01
    • 2014-07-29
    • 1970-01-01
    • 1970-01-01
    • 2021-03-24
    相关资源
    最近更新 更多