在Objective-C中做事情的正确位置
我开始玩弄ObjectiveFlickr框架,目的是创建一个相对简单的iPhone地图应用程序,显示当前MKMapView区域内的地理标记flickr内容。我遇到了与线程相关的问题,现在我感觉我的体系结构中出现了一些根本性的错误。基本上我所拥有的是:在Objective-C中做事情的正确位置,objective-c,cocoa-touch,Objective C,Cocoa Touch,我开始玩弄ObjectiveFlickr框架,目的是创建一个相对简单的iPhone地图应用程序,显示当前MKMapView区域内的地理标记flickr内容。我遇到了与线程相关的问题,现在我感觉我的体系结构中出现了一些根本性的错误。基本上我所拥有的是: 一个主视图控制器,用于创建 并处理MKMapView对象和 钮扣 按一下按钮就可以打电话了 调用Flickr API的方法 用于在 当前地图范围 回调 此API调用的方法将进行迭代 通过结果并将其放入 FlickrImage对象的NSMutable
-(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类中这样做,但现在它变得丑陋了:
- MainViewController在每次迭代中创建FlickrImage的一个实例,并将其存储在NSMutableArray中李>
- FlickrImage实例不连续地调用地理位置Flickr API,并应将返回的坐标存储在相应的成员变量中
malloc: *** error for object 0x451bc04: incorrect checksum for freed object - object was probably modified after being freed.
在我的调试输出中,几乎总是出现一个EXC\u BAD\u访问
,但并不总是在同一点上
很明显,我在这里犯了一些根本性的错误,但是什么呢?这个错误的意思正是它所说的:您可能修改了内存 释放后,EXC_BAD_访问表示您试图访问一个不存在的数组元素。我会检查你的FlickrImage对象在你尝试调用地理定位方法时没有被解除锁定 在您的设计中,没有任何东西是有根本缺陷的
@techzen-这就是我缺少Xcode的地方 /gdb技能。我如何记录这些?那个 这确实会提供有用的见解 对于NSObject固有的类,您可以让NSLog直接打印该对象
NSLog(@"myObject=%@", myObjectInstance);
NSLog将调用instances debugDescription方法,该方法通常会打印出如下内容:
<MyObjectClass 0x451bc04>
NSString *dictKey;
NSDictionary *photoDictionary;
for (dictKey in photosDictionary) {
photoDictionary=[photosDictionary valueForKey:dictKey];
...
“%p”格式说明符就是诀窍。你可能想充实一下,比如:
NSLog(@"<%@ %p>", [myObjectInstance class], myObjectInstance);
// prints <MyObjectClass 0x451bc04>
NSLog(@“,[myObjectInstance类],myObjectInstance);
//印刷品
将这些语句放在创建对象的位置,当发生错误时,通过查看调试器控制台可以看到哪些对象失败。我认为主要问题是没有为字典正确配置快速枚举循环。与数组不同,字典上的快速枚举只返回键,而不返回值。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) {
例如:
<MyObjectClass 0x451bc04>
NSString *dictKey;
NSDictionary *photoDictionary;
for (dictKey in photosDictionary) {
photoDictionary=[photosDictionary valueForKey:dictKey];
...
你不需要释放photoDictionary,因为你不需要创建一个新的对象,你只需要获得一个对它的引用 当您迭代字典时,无需调用
[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
了解更多说明。您知道什么是对象0x452bc04吗?如果您记录对象的地址,特别是FlickrImage对象的地址,那么您可以查看调试器地址,至少可以看到哪个类失败。您应该粘贴最后两个步骤(创建、存储和修改FlickrImage)的代码。@kai1968-添加的代码snippets@techzen-这就是我缺乏Xcode/gdb技能的地方。我如何记录这些?这将提供有用的见解。谢谢。你的照片字典发布得太多了。不要在for循环结束时调用[photoDictionary release],你不拥有该实例。+1。并使用NSZombieEnabled找出哪一个对象是罪魁祸首。@ennuikiller:谢谢你的安慰;)这就是我相信正在发生的事情,我很想检查一下,但是我怎么做呢?顺便说一下,我添加了一些代码片段,这可能会澄清一些问题。谢谢嗯,有趣的一点。但在我当前的代码中,NSLog(@“photodictionary is%@,[photodictionary description]);输出看似有效的字典对象描述,例如:photodictionary为{farm=3;id=4173994142;isfamily=0;isfriend=0;ispublic=1;owner=”37727710@N08“secret=75f2885f80;server=2521;title=“Dit是een kunstwerk”}您是否将photodictionary的键设置为photodictionary对象?如果反转键和值,则很容易做到这一点。您应该通过[photodictionary valueForKey:photodictionary]
记录返回的值,并查看得到的结果。感谢您提供了这些宝贵的指针。我认为这类材料在苹果的开发文档中没有得到很好的介绍,或者我忽略了这里的内容?谢谢stefanB,其他人也指出了这一点,但我感谢你详细阐述这个主题。我一定会多读一些关于内存管理的书,这肯定是我ObjC技能中的一个弱点——我没有C/C++方面的背景,但只懂Java和.NET之类的垃圾收集语言,我只是不习惯于这样做