在Objective-C中做正确的事情

时间:2009-12-10 15:24:16

标签: objective-c cocoa-touch

我开始研究ObjectiveFlickr框架,目的是创建一个相对简单的iPhone地图应用程序,显示当前MKMapView区域内的地理标记flickr内容。我遇到线程相关的麻烦before,现在我感觉我的架构中出现了根本性的错误。基本上我所拥有的是:

  1. 创建的MainViewController 并处理MKMapView对象和 一个按钮
  2. 点按按钮电话 一种调用Flickr API的方法 对于地理标记内的照片 当前地图范围。
  3. 回调 此API调用的方法迭代 通过结果并将它们放入 一个FlickrImage对象的NSMutableArray。 FlickrImage是一个简单的数据类,它将flickr图像位置保存为CLLocation,指向缩略图的NSURL和标题NSString。
  4. 第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类中做到这一点,但是这里很难看:

    • MainViewController每次迭代都会创建一个FlickrImage实例,并将其存储在NSMutableArray中。
    • FlickrImage实例非常响应地调用地理定位Flickr API,并且应该将相应成员变量中返回的坐标存储起来。

    我很确定这不会发生,因为我正在

    malloc: *** error for object 0x451bc04: incorrect checksum for freed object - object was probably modified after being freed.
    

    洒在我的调试输出周围,几乎总是EXC_BAD_ACCESS,但不是始终在同一点。

    我显然在这里做了一些根本错误的事情,但是什么呢?

4 个答案:

答案 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,因为您不创建新对象,只需获取对它的引用即可。