objc_setAssociatedObject函数在64位模式下的错误不在32位

我在我的项目中使用一个名为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);

还有这样的:打印*(subRowObj)的描述:(id)[0] =打印SubRowObjectKey的描述:

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

  #pragma mark  -  NSIndexPath(SKSTableView)
 static void * SubRowObjectKey;
 @implementation NSIndexPath(SKSTableView)
 @dynamic subRow;
 - (NSInteger)子行
 {
     id subRowObj = objc_getAssociatedObject(self,SubRowObjectKey);
     return [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 = subrow;   
    返回indexPath;
 }

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

看来, objc_setAssociatedObject()不能与“标记的指针”。 标记指针是指向某些东西的特殊指针,但“值”存储在指针本身的某些位上。 请参阅Objective-C中的Tagged指针 ,该链接指向更多信息。

带标记的指针仅用于64位代码,例如,用于具有较小行号和段号的索引path或小号对象。

可以很容易地validation在iOS 7/64-bit上使用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 。 我认为这是值得向苹果公司的错误报告。

(备注:在OS X / 64位上不会发生此问题。)


更新 (可能的解决scheme/解决方法):“SKSTableView”项目(问题结尾的链接)使用NSIndexPath和关联对象上的类别向索引path添加第三个属性“subrow”。 这个子行是暂时设置的

 tableView:cellForRowAtIndexPath: 

SKSTableView将当前子行传递给

 tableView:cellForSubRowAtIndexPath:indexPath 

ViewController类中的委托方法。

如果使用“正确的”三层索引path,则不需要关联的对象,示例应用程序也以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; } 

在以下方法中更改设置:

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

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

我碰到这个,但在另一种情况下,我需要添加一个属性到标记的指针types。

这是我想出来的。 它使用NSMapTable弱ToStrongObjectsMapTable。 它甚至可以作为objc_setAssociatedObject / objc_getAssociatedObject的标签指针和常规对象types的插入replace:

 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 
Interesting Posts