【问题标题】:Problems with custom delegates自定义委托的问题
【发布时间】:2011-09-26 11:14:14
【问题描述】:

这是我第一次尝试编写自己的委托。我正在使用 iOS5 Beta 7 并编写我的委托函数。

它应该做的是一个表 (MyTableController) 加载一个电视频道列表。这是一个包含键“logotype”的字典,它是图像标识的 URL。 我创建了名为“Channel”的类,我的代表名为“ChannelDelegate”。现在,当我的表执行cellForRowAtIndexPath 时,它会启动我的Channel 类并调用函数getChannelImageForChannelId:externalRefference:indexPath。到目前为止一切顺利,现在我的频道类检查文件是否在本地存在,如果不存在,它将下载它(使用ASIHttpRequest)。此外,到目前为止一切顺利。现在,当 ASIHttpRequest 在 requestDidFinish 返回时,它会在一些结果后死亡。我注意到该文件确实被下载了,因为经过几次尝试它就像一个魅力,因此我的代表似乎工作。

这是我的代码:
CellForRowAtIndexPath:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"CurrentChannelInfoCell";

    CurrentChannelInfoCell *cell = (CurrentChannelInfoCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil){
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:nil options:nil];

        for(id currentObject in topLevelObjects)
        {
            if([currentObject isKindOfClass:[CurrentChannelInfoCell class]])
            {
                cell = (CurrentChannelInfoCell *)currentObject;
                break;
            }
        }
    }


    NSArray *keys = [self.content allKeys];
    id channelId = [keys objectAtIndex:indexPath.row];
    NSDictionary *channel = [self.content objectForKey:channelId];

    int cId = [(NSString*)channelId intValue];

    [cell.textLabel setText:[channel objectForKey:@"name"]];
    [cell.channelImage setHidden:YES];

    Channel *theChannel = [[Channel alloc] init];
    [theChannel setDelegate:self];
    [theChannel getChannelImageForChannelId:cId externalRefference:[channel objectForKey:@"logotype"] indexPath:indexPath];

    return cell;
}

- (void)didRecieveImageForChannel:(NSString*)imagePath indexPath:(NSIndexPath*)indexPath
{
    NSLog(@"Did recieve the it.!");
}

我的代表,ChannelDelegate.h

#import <Foundation/Foundation.h>

@class Channel;

@protocol ChannelDelegate <NSObject>

@optional
- (void)didRecieveImageForChannel:(NSString*)imagePath indexPath:(NSIndexPath*)indexPath;

@end

Channel.h:

#import <Foundation/Foundation.h>
#import "ChannelDelegate.h"

#import "Reachability.h"
#import "ASIHTTPRequest.h"
#import "ASINetworkQueue.h"
#import "ASIFormDataRequest.h"

@interface Channel : NSOperation <NSObject, ASIHTTPRequestDelegate>
{   
    ASINetworkQueue *networkQueue;

    // Called on the delegate (if implemented) when the request completes successfully.
    id <ChannelDelegate> delegate;

    SEL didRecieveImageForChannelSelector;
}

@property (strong, nonatomic) id delegate;
@property (assign) SEL didRecieveImageForChannelSelector;

- (void)getChannelImageForChannelId:(int)channelId externalRefference:(NSString*)url indexPath:(NSIndexPath*)indexPath;
- (id)delegate;

@end

Channel.m:

#import "Channel.h"
#import <objc/runtime.h>

@implementation Channel

static char kAssociationKey;

@synthesize didRecieveImageForChannelSelector;
@synthesize delegate;

- (void)getChannelImageForChannelId:(int)channelId externalRefference:(NSString*)url indexPath:(NSIndexPath*)indexPath
{
    [self setDidRecieveImageForChannelSelector:@selector(didRecieveImageForChannel:indexPath:)];

    NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];

    NSString *imagePath = [documentsPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%d.gif", channelId]];
    BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:imagePath];

    if (fileExists)
    {
        NSLog(@"Downloaded image!");
        [[self delegate] performSelector:[self didRecieveImageForChannelSelector] withObject:imagePath withObject:indexPath];
    }
    else {
        NSLog(@"Need to fetch it.!");

        NSURL *theUrl = [NSURL URLWithString:url];

        ASINetworkQueue *newQueue = [[ASINetworkQueue alloc] init];
        [newQueue setRequestDidFinishSelector:@selector(channelImageFetched:)];
        [newQueue setRequestDidFailSelector:@selector(processFailed:)];
        [newQueue setDelegate:self];

        ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:theUrl];
        [request setTimeOutSeconds:60];
        [request setDownloadDestinationPath:imagePath];
        [newQueue addOperation:request];

        objc_setAssociatedObject(request, &kAssociationKey, indexPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

        [newQueue go];
    }
}

- (id)delegate
{
    id d = delegate;
    return d;
}

- (void)setDelegate:(id)newDelegate
{
    delegate = newDelegate;
}

// Handle process two
- (void)channelImageFetched:(ASIHTTPRequest *)request 
{
    NSLog(@"channelImageFetched!");


    NSIndexPath *indexPath = objc_getAssociatedObject(request, &kAssociationKey);
    NSString *imagePath = request.downloadDestinationPath;

    [[self delegate] performSelector:[self didRecieveImageForChannelSelector] withObject:imagePath withObject:indexPath];    

    NSLog(@"File downloaded!");
}

- (void) processFailed:(ASIHTTPRequest *)request
{   
    NSError *error = [request error];

    UIAlertView *errorView;

    errorView = [[UIAlertView alloc]
                 initWithTitle: NSLocalizedString(@"Whoops!", @"Errors")
                 //              message: NSLocalizedString(@"An error occured while preforming the request. Please try again.", @"Network error")
                 message: [error localizedDescription]
                 delegate: self
                 cancelButtonTitle: NSLocalizedString(@"Close", @"Errors") otherButtonTitles: nil];

    [errorView show];

    /*  NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
     [request error], @"Error",
     [request url], @"URL",
     [[NSDate alloc] init], @"timestamp", nil];

     [FlurryAPI logEvent:@"APNS-Could not fetch data." withParameters:dictionary timed:YES];
     */
}

@end

我收到的错误似乎有时会有所不同,但这些似乎是我得到的错误:

2011-09-26 13:11:57.605 TVSports[4541:ef03] 完成!
2011-09-26 13:11:57.609 TVSports[4541:ef03] 下载图片!
2011-09-26 13:11:57.610 TVSports[4541:ef03] 收到了!
2011-09-26 13:11:57.613 TVSports[4541:ef03] 需要获取 如果.!
2011-09-26 13:11:57.616 TVSports[4541:ef03] 需要 拿来吧!
2011-09-26 13:11:57.618 TVSports[4541:ef03] 需要拿它!
2011-09-26 13:11:57.621 TVSports[4541:ef03] 需要获取它!
2011-09-26 13:11:57.624 TVSports[4541:ef03] 需要获取它。!
2011-09-26 13:11:57.629 TVSports[4541:ef03] 需要获取 它。!
2011-09-26 13:11:57.633 TVSports[4541:ef03] 需要 拿来吧!
2011-09-26 13:11:57.663 TVSports[4541:ef03] 需要拿它!
2011-09-26 13:11:57.669 TVSports[4541:ef03] 需要获取它!
2011-09-26 13:11:57.846 TVSports[4541:ef03]-[UIDeviceWhiteColor channelImageFetched:]: 无法识别的选择器发送到实例 0x65a1e30
2011-09-26 13:11:57.847 TVSports[4541:ef03] * 由于未捕获的异常而终止应用程序 'NSInvalidArgumentException',原因:'-[UIDeviceWhiteColor channelImageFetched:]: 无法识别的选择器发送到实例 0x65a1e30'
*
第一次抛出调用栈:
(0x15da272 0x1769ce6 0x15dbf0d 0x1540e2f 0x1540c12 0x15dc092 0x3adf8 0x15dc092 0x24c43 0x15dc092 0xef9fac 0x15aec0f 0x15118c3 0x15111a4 0x1510b04 0x1510a1b 0x1ce6f67 0x1ce702c 0x608cf2 0x2718 0x2675 0x1)
终止调用抛出 异常(gdb)

或者我在 ASIHTTPRequest 的一行中得到一个 EXC_BAD_ACCESS(我无法重现最近 10 次尝试。)

有人能看出我做错了吗?我的代码哪里搞砸了?

【问题讨论】:

    标签: iphone ios delegates asihttprequest ios5


    【解决方案1】:

    [UIDeviceWhiteColor channelImageFetched:]: unrecognized selector sent to instance 0x65a1e30 -- 这告诉您“channelImageFetched”“消息”已“发送”到 UIDeviceWhiteController 类型的对象。 (即,调用了 UIIDeviceWhiteController ->channelImageFetched。)并且 UIDeviceWhiteController 没有名为“channelImageFetched”的方法。

    虽然这可能是您将指针混淆的问题,但更有可能实现“channelImageFetched”的对象(您的对象)被过度释放和释放(您可以通过在您的dealloc 例程。)所以你的问题很可能是一个常见的存储管理错误。

    了解委托的存储管理是一件棘手的事情。通常,当一个对象被交给一个委托对象时,它应该保留它并一直保留它,直到调用了所有可能的委托方法,此时第一个对象应该释放委托对象。但当然,您有责任在将委托对象设置为委托之前保留委托对象,如果需要,在调用所有委托方法之后。

    [顺便说一句,虽然 cmets 和诊断消息中的幽默总是受欢迎的,但在您可能暴露给他人的任何代码中使用“motherf...”是相当不专业的。]

    【讨论】:

    • 感谢您的回答。我了解您对我的代码 cmets 的评论,..您是对的。然而,在没有运气的情况下挖了两个小时后,这些日志语句往往会出现,我倾向于停止关心。关于你的dealoc explenation,这可能是一个问题,因为在iOS 5 中使用新的ARC 时dealoc 已被弃用。还有什么其他方法可以“保留”我的对象?
    【解决方案2】:

    好吧,我仍然不知道发生了什么.. 但似乎我找到了解决办法。

    在 mu Channel.m 文件中,我稍微更改了内容。而不是使用NetworkQueue,我现在只是在请求对象上执行startAsynchronous

    最终结果:

    [...]
        if (fileExists)
        {
            NSLog(@"Downloaded image!");
            [[self delegate] performSelector:[self didRecieveImageForChannelSelector] withObject:imagePath withObject:indexPath];
        }
        else {
            NSLog(@"Need to fetch the image!");
    
            NSURL *theUrl = [NSURL URLWithString:url];
    
            ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:theUrl];
            [request setDidFinishSelector:@selector(channelImageFetched:)];
            [request setDidFailSelector:@selector(processFailed:)];
            [request setTimeOutSeconds:60];
            [request setDownloadDestinationPath:imagePath];
    
            objc_setAssociatedObject(request, &kAssociationKey, indexPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
            [request startAsynchronous];
        }
    [...]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-17
      • 1970-01-01
      • 1970-01-01
      • 2020-02-09
      • 1970-01-01
      • 2012-12-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多