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