我开始研究ObjectiveFlickr框架,目的是创建一个相对简单的iPhone地图应用程序,显示当前MKMapView区域内的地理标记flickr内容。我遇到线程相关的麻烦before,现在我感觉我的架构中出现了根本性的错误。基本上我所拥有的是:
第2步的代码段:
-(void)actionSearchForTripodPhotos {
if(currentBoundingBox == nil) {
// TODO add a messagebox saying we're waiting for location info - or just lock the app until we're sure.
return;
}
NSString *dateTakenMinimumUNIXTimeStampString = [NSString stringWithFormat:@"%f",[[NSDate dateWithTimeIntervalSinceNow:-100000] timeIntervalSince1970]];
OFFlickrAPIRequest *flickrAPIRequest = [[OFFlickrAPIRequest alloc] initWithAPIContext:[CloudMadeMap101AppDelegate sharedDelegate].flickrAPIContext];
[flickrAPIRequest setDelegate:self];
NSString *flickrAPIMethodToCall = @"flickr.photos.search";
NSString *bboxString = [NSString stringWithFormat:@"%f,%f,%f,%f",currentBoundingBox.bottomLeftLat ,currentBoundingBox.bottomLeftLon ,currentBoundingBox.topRightLat ,currentBoundingBox.topRightLon];
NSLog(@"bounding box to be sent to flickr: %@",bboxString);
NSDictionary *requestArguments = [[NSDictionary alloc] initWithObjectsAndKeys:FLICKR_API_KEY,@"api_key",[NSString stringWithFormat:@"%f",currentLocation.coordinate.latitude],@"lat",[NSString stringWithFormat:@"%f",currentLocation.coordinate.longitude],@"lon",dateTakenMinimumUNIXTimeStampString,@"min_upload_date",nil];
[flickrAPIRequest callAPIMethodWithGET:flickrAPIMethodToCall arguments:requestArguments];
}
第3步的代码段:
- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didCompleteWithResponse:(NSDictionary *)inResponseDictionary {
NSDictionary *photosDictionary = [inResponseDictionary valueForKeyPath:@"photos.photo"];
NSDictionary *photoDictionary;
FlickrImage *flickrImage;
for (photoDictionary in photosDictionary) {
NSLog(@"photodictionary is %@",[photoDictionary description]);
flickrImage = [[FlickrImage alloc] init];
flickrImage.thumbnailURL = [[appDelegate sharedDelegate].flickrAPIContext photoSourceURLFromDictionary:photoDictionary size:OFFlickrThumbnailSize];
flickrImage.hasLocation = TRUE; // TODO this is actually to be determined...
flickrImage.ID = [NSString stringWithFormat:@"%@",[photoDictionary valueForKeyPath:@"id"]];
flickrImage.owner = [photoDictionary valueForKeyPath:@"owner"];
flickrImage.title = [photoDictionary valueForKeyPath:@"title"];
[flickrImages addObject:flickrImage];
[photoDictionary release];
}
}
一切顺利。 API烦人地不会为每张照片返回地理位置,因此这需要另一个API调用。我想我可以在FlickrImage类中做到这一点,但是这里很难看:
我很确定这不会发生,因为我正在
malloc: *** error for object 0x451bc04: incorrect checksum for freed object - object was probably modified after being freed.
洒在我的调试输出周围,几乎总是EXC_BAD_ACCESS
,但不是始终在同一点。
我显然在这里做了一些根本错误的事情,但是什么呢?
答案 0 :(得分:6)
错误意味着它所说的内容:你可能已经修改了内存 释放后,EXC_BAD_ACCESS表示您正在尝试访问不存在的数组元素。当你试图调用地理定位方法时,我会检查你的FlickrImage对象是不是被释放了。
你的设计中没有任何东西能够脱颖而出,具有根本性的缺陷
答案 1 :(得分:2)
@techzen - 这是我缺少Xcode的地方 / gdb技能。我该如何记录?那 确实会提供有用的见解。
对于NSObject固有的类,您只需让NSLog直接打印该对象。
NSLog(@"myObject=%@", myObjectInstance);
NSLog将调用实例debugDescription方法,该方法通常会打印出类似的内容:
<MyObjectClass 0x451bc04>
有些类会生成更多信息,但有时它们不提供实例地址,因此您必须直接打印它:
NSLog(@"myObject's Address=%p",myObjectInstance);
“%p”格式说明符就是技巧。你可能想要充实它有点像:
NSLog(@"<%@ %p>", [myObjectInstance class], myObjectInstance);
// prints <MyObjectClass 0x451bc04>
将这些语句放在创建对象的位置,当发生错误时,您可以通过查看调试器控制台来查看哪些对象失败。
答案 2 :(得分:2)
当您在字典上进行迭代时,无需调用[photoDictionary release]
:
NSDictionary *photosDictionary =
[inResponseDictionary valueForKeyPath:@"photos.photo"];
NSDictionary *photoDictionary;
FlickrImage *flickrImage;
for (photoDictionary in photosDictionary) {
...
[photoDictionary release];
我认为这就是你的问题所在。
当调用release
并且对象到达ref count 0
时,它将被取消分配。
因为您不应该这样做,稍后在发布字典时它会向其每个元素发送release
,但您可能已经取消分配它们。
这是objective-c 中的基本内存管理。查看内存管理和retain/release/autorelease
内容以获得更多解释。
答案 3 :(得分:1)
我认为主要问题是没有为字典正确配置快速枚举循环。与数组不同,字典上的快速枚举仅返回键,而不返回值。 e.g。
NSDictionary *a=[NSDictionary dictionaryWithObjectsAndKeys:@"bob",@"bobKey",@"steve",@"steveKey",nil];
NSDictionary *b=[NSDictionary dictionaryWithObjectsAndKeys:@"bob1",@"bobKey",@"steve1",@"steveKey",nil];
NSDictionary *c=[NSDictionary dictionaryWithObjectsAndKeys:a,@"a",b,@"b",nil];
NSDictionary *d;
for (d in c) {
NSLog(@"[d class]=%@,[d description]=%@",[d class],d);
NSLog(@"[c valueForKey:d]=%@",[c valueForKey:d]);
}
打印:
[d class]=NSCFString,[d description]=a
[c valueForKey:d]={
bobKey = bob;
steveKey = steve;
}
[d class]=NSCFString,[d description]=b
[c valueForKey:d]={
bobKey = bob1;
steveKey = steve1;
}
请注意,即使d
被定义为NSDictionary,它仍然被指定为键的字符串值。
您需要更改:
NSDictionary *photoDictionary;
FlickrImage *flickrImage;
for (photoDictionary in photosDictionary) {
类似于:
NSString *dictKey;
NSDictionary *photoDictionary;
for (dictKey in photosDictionary) {
photoDictionary=[photosDictionary valueForKey:dictKey];
...
并且您不需要发布photoDictionary,因为您不创建新对象,只需获取对它的引用即可。