在Objective-C中做事情的正确位置

在Objective-C中做事情的正确位置,objective-c,cocoa-touch,Objective C,Cocoa Touch,我开始玩弄ObjectiveFlickr框架,目的是创建一个相对简单的iPhone地图应用程序,显示当前MKMapView区域内的地理标记flickr内容。我遇到了与线程相关的问题,现在我感觉我的体系结构中出现了一些根本性的错误。基本上我所拥有的是: 一个主视图控制器,用于创建 并处理MKMapView对象和 钮扣 按一下按钮就可以打电话了 调用Flickr API的方法 用于在 当前地图范围 回调 此API调用的方法将进行迭代 通过结果并将其放入 FlickrImage对象的NSMutable

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

  • 一个主视图控制器,用于创建 并处理MKMapView对象和 钮扣
  • 按一下按钮就可以打电话了 调用Flickr API的方法 用于在 当前地图范围
  • 回调 此API调用的方法将进行迭代 通过结果并将其放入 FlickrImage对象的NSMutableArray。FlickrImage是一个简单的数据类,它将flickr图像位置保存为CLLocation、指向缩略图的NSURL和标题NSString
  • 步骤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\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之类的垃圾收集语言,我只是不习惯于这样做