我在我的项目中使用一个名为 SKSTableView 的整洁的表视图控制器,它允许每个表行具有多个子行。此代码在 32位模式下运行良好,但是当我在iPhone 5S或4英寸64位模式的模拟器中运行时,当您点击一行以获取子行时,它会崩溃。我对64位和32位iOS系统的差异一无所知。我很想知道这里发生了什么。
您会注意到* SubRowObjectKey设置为void-我得到的错误是:
EXC_BAD_ACCESS_(代码= 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
答案 0 :(得分:12)
似乎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。我认为这值得一个bug 向Apple报告。
(注:OS X / 64位上不会出现此问题。)
更新 (可能的解决方案/解决方法):“SKSTableView”项目(问题末尾的链接)使用类别
NSIndexPath
和相关对象将第三个属性“subrow”添加到索引路径。
此子目录暂时设置在
tableView:cellForRowAtIndexPath:
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;
}
答案 1 :(得分: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
答案 2 :(得分: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