iOS如何修复一个数组,该数组返回一个找不到的对象的索引
Posted
技术标签:
【中文标题】iOS如何修复一个数组,该数组返回一个找不到的对象的索引【英文标题】:iOS how do I fix an array returning an index for an object that can't be found 【发布时间】:2014-02-20 22:26:23 【问题描述】:我的数组正在为我正在寻找的对象的索引返回一个不正确的数字。我的 array.count 告诉我数组中有 248 个对象,但返回的索引是 2147483647。我看到了与我的问题类似的 post。我使用了这篇文章中提出的平等测试,但没有弄清楚为什么会发生这种情况以及如何解决它。
这是我的代码:(已编辑解决方案)
-(void)getChamberPrimaryCategories
NSString *docsDir;
NSArray *dirPaths;
chamberCategoryArray = [[NSMutableArray alloc] init];
// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = dirPaths[0];
// Build the path to the database file
_databasePath = [[NSString alloc]
initWithString: [docsDir stringByAppendingPathComponent:
@"the_app.db"]];
NSLog(@"%@",_databasePath);
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: _databasePath ] == NO)
const char *dbpath = [_databasePath UTF8String];
if (sqlite3_open(dbpath, &_the_app_database) == SQLITE_OK)
//do nothing
else
NSLog(@"Failed to open database");
const char *dbpath = [_databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &_the_app_database) == SQLITE_OK)
NSString *querySQL;
querySQL = @"SELECT DISTINCT CHAMBER_PRIMARY_CATEGORY FROM PLACES WHERE CHAMBER_PRIMARY_CATEGORY IS NOT '' AND CHAMBER_PRIMARY_CATEGORY IS NOT NULL ORDER BY CHAMBER_PRIMARY_CATEGORY ASC";
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(_the_app_database,
query_stmt, -1, &statement, NULL) == SQLITE_OK)
NSLog(@"Query the database now.");
// if (sqlite3_step(statement) != SQLITE_ROW)
// NSLog(@"Not okay");
//
while (sqlite3_step(statement) == SQLITE_ROW)
NSLog(@"Getting Information:");
placesChamber *chamberCategoryObject = [[placesChamber alloc]init];
NSString *placesPrimaryChamberCategory = [[NSString alloc]
initWithUTF8String:
(const char *) sqlite3_column_text(
statement, 0)];
chamberCategoryObject.primary_category = placesPrimaryChamberCategory;
NSLog(@"%@",placesPrimaryChamberCategory);
[chamberCategoryArray addObject:chamberCategoryObject];
//end while
//end if
else
NSLog(@"There is nothing in this database!");
NSLog(@"%s",sqlite3_errmsg(_the_kearney_app_database));
sqlite3_finalize(statement);
sqlite3_close(_the_kearney_app_database);
[chamberCategoryTableView reloadData];
NSLog(@"content offset: %f",chamberCategoryTableView.contentOffset.y);
if (chamberCategoryTableView.contentOffset.y == 0)
placesChamber *searchChamber = [[placesChamber alloc] init];
searchChamber.primary_category = @"Women's Clothing";
NSUInteger index = [chamberCategoryArray indexOfObject:searchChamber];
if (index == NSNotFound)
// no such chamber category
NSLog(@"no such thing found");
else
// Found it at index
NSLog(@"found it!");
//end if
//end if
这是我的地方Chamber.m:(需要解决方案)
#import "placesChamber.h"
@implementation placesChamber
@synthesize ID;
@synthesize name;
@synthesize type;
@synthesize category;
@synthesize latitude;
@synthesize longitude;
@synthesize primary_category;
@synthesize secondary_category;
- (BOOL)isEqual:(id)object
if ( self == object )
return YES;
if ( ![object isKindOfClass:[placesChamber class]] )
return NO;
if ( ![primary_category isEqualToString:[object primary_category]] )
return NO;
return YES;
@end
【问题讨论】:
这是NSNotFound
的值。这意味着您的对象不在数组中。
只检查索引是否为!=NSNotFound
然后用它做你想要的,否则它在你的数组中找不到。
BTW - 您的数组包含 placesChamber
对象的实例,但您正在寻找 NSNumber
对象和 NSString
文字。那永远行不通。
@rmaddy 我知道这是 NSNotFound 的价值,但为什么我得到它?我将 NSNumber 的问题改回了应有的状态。顺便感谢您查看我的问题。
顺便说一句,调试器变量视图非常不可靠。确定值是什么的唯一方法是使用 NSLog,或者在控制台中使用 po
。
【参考方案1】:
正如我在 cmets 中所述,您已使用 placesChamber
对象的实例填充了您的 chamberCategoryArray
。
但是您随后尝试查找其中一个对象的索引,而不是通过传入 placesChamber
对象,而是通过传入 NSString
文字。那根本行不通。
你需要做这样的事情:
placesChamber *searchChamber = [[placesChamber alloc] init];
searchChamber.primary_category = @"Women's Clothing";
NSUInteger index = [chamberCategoryArray indexOfObject:searchChamber];
if (index == NSNotFound)
// no such chamber category
else
// Found it at index
此代码假定您已在您的placesChamber
类上实现了isEqual:
方法,该方法比较primary_category
属性。
旁注:标准约定是类名以大写字母开头,而方法名和变量以小写字母开头。都应该使用驼峰式。
鉴于此,您的类应为PlacesChamber
,属性应为primaryCategory
。
【讨论】:
我添加了这个:placesChamber *searchChamber = [[placesChamber alloc] init]; searchChamber.primary_category = @"Women's Clothing"; NSUInteger index = [chamberCategoryArray indexOfObject:searchChamber]; if (index == NSNotFound) // no such chamber category NSLog(@"no such thing found"); else // Found it at index NSLog(@"found it!");
我仍然返回了 NSNotFound。
通过在 placesChamber
类上实现 isEqual:
方法来更新您的问题。
更新了我的问题。希望这就是您想要的。
鉴于您的isEqual:
方法的实现,它应该可以工作。为了安全起见,添加hash
方法:- (NSUInteger)hash return [self.primary_category hash];
。
如果没有isEqual:
方法,它只会找到具有相同地址的对象。【参考方案2】:
我发现你在以下3行代码中使用了indexOfObject:
:
//1.
NSIndexPath *scrollIndexPath = [NSIndexPath indexPathForItem:[chamberCategoryArray indexOfObject:[defaults objectForKey:@"chamber filter category"]] inSection:0];
//2.
NSLog(@"index number: %lu",(unsigned long)[chamberCategoryArray indexOfObject:@"Women's Clothing"]);
//3.
if(NSNotFound == [chamberCategoryArray indexOfObject:[NSNumber numberWithInteger:1]])
NSLog(@"not found");
您正在以下行中填充您的chamberCategoryArray
,该行使用placesChamber
类型的对象填充数组:
[chamberCategoryArray addObject:chamberCategoryObject];
现在,如果您将indexOfObject:
消息传递给NSArray
(或其子类),它会遍历数组,向数组的每个对象发送isEqual:
消息以表示相等,如果找到,则返回索引,否则返回NSNotFound
(如您所见)。并且这种相等比较是基于为每个包含的对象计算的哈希码,也就是说,如果数组中的一个对象与它正在比较的对象具有相同的哈希码(内部计算),则将它们视为相等。
在您的示例代码中:
在 1. 您正在检查数组对象的哈希码 使用[defaults objectForKey:@"chamber filter
category"]
的哈希码,我不确定defaults
是由什么组成的。
在 2. 中,您正在对照 NSString
检查元素。但是
数组对象的类型为placesChamber
,哈希码为
两种不同的类型永远不会匹配,你总是会得到NSNotFound
结果。
在 3. 中你也犯了同样的错误,这次是NSNumber
而不是NSString
。所以上面的逻辑在这里也成立。
因此很明显,您偶尔会收到NSNotFound
(2147483647)。
我相信您正在尝试从具有与您的测试标准匹配的属性的数组中返回对象的索引(实际上是 placesChamber
按类型)。为此,您可以使用 indexOfObjectPassingTest:
方法,该方法需要一个块来编写您的比较逻辑。在该块中,您可以编写自己的自定义逻辑,将数组项与您的搜索条件进行匹配,并相应地返回对象索引。
更新
这就是您真正要寻找的东西:
NSString* criteria = @"Women's Clothing";
int index = [chamberCategoryArray indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop)
placesChamber* item = obj;
*stop = [item.primary_category isEqualToString:criteria];
return *stop;
];
这里我演示了如何根据placesChamber
对象的primary_category
属性获取索引。同样的方法也适用于其他属性。
【讨论】:
您对indexOfObject:
的工作方式不正确。当您说为每个对象调用 isEqual:
时,您是正确的。但这就是所做的一切。哈希在这方面根本没有任何作用。如果isEqual:
方法返回YES
,则认为这两个对象相等。就是这样。所以这一切都取决于所涉及对象的isEqual:
方法的正确实现。由于 OP 有一个包含一种类型对象的数组并且正在寻找另一种类型的对象,isEqual:
将永远不会返回YES
。
@rmaddy isEqual:
与 hash
严格相关,如果没有被覆盖的话。实际上,如果没有被覆盖,isEqual:
的默认实现会自行比较两个对象的哈希码是否相等,由hash
方法返回。详情请至the link
是的,isEqual:
和 hash
应该始终成对编写,这样它们才能正常工作。是的,两者的默认值基本上都是对象地址。这些都不相关。您的回答一直强调已检查哈希。它不是。调用isEqual:
方法。就是这样。如果碰巧isEqual:
的默认实现调用hash
,那就是无关紧要的次要问题。在这种情况下,用户已经(或需要)覆盖isEqual:
以根据需要比较对象的属性。完成后,hash
将在这段代码中没有任何作用。
@rmaddy 再次,我希望有所不同。:( hash
与对象的地址无关,而是取决于内容和内部哈希算法。为了满足,请创建两个 NSString
, str1
和str2
并为它们设置相同的值,如@"Hello"
。现在如果将它们与isEqual:
进行比较,您会发现它返回YES
,尽管对象具有不同的地址。至于我的关于答案,我刚刚试图澄清isEqual
在内部的行为方式。但是你有点正确,我可能解释过度了。谢谢:)
如我所说,默认(NSObject
)使用对象地址。 NSString
覆盖 hash
和 isEqual:
以按预期工作。 NSString
没有使用地址。您的 NSString
示例不相关。一个更好的例子是创建两个 NSObject
实例。以上是关于iOS如何修复一个数组,该数组返回一个找不到的对象的索引的主要内容,如果未能解决你的问题,请参考以下文章