自定义委托的问题

发布于 2024-12-06 06:52:49 字数 7609 浏览 1 评论 0原文

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

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

这是我的代码:
The 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.!");
}

My Delegate, 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 次尝试。)

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

This is one of my first times trying to write my own delegates. I am using iOS5 Beta 7 and writing my delegate function.

What it should do is that a table (MyTableController) loads a list of TV Channels. This is a dictionary containing the key "logotype", which is a URL to the logotype of the image.
I created my class called "Channel" and my delegate called "ChannelDelegate". Now, when my table does cellForRowAtIndexPath it initiates my Channel class and calls the function getChannelImageForChannelId:externalRefference:indexPath. So far so good, now my channel class check if the file exists locally, if not it will download it (using ASIHttpRequest). Also, so far so good. Now when ASIHttpRequest returns on requestDidFinish it dies after a few results. I notice that the file did get downloaded because after a few tries it works like a charm, hence my delegate seems to work.

This is my code:
The 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.!");
}

My Delegate, 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

The error I recieve seems to differ from time to time, however these seem the errors I got:

2011-09-26 13:11:57.605 TVSports[4541:ef03] Done!
2011-09-26
13:11:57.609 TVSports[4541:ef03] Downloaded image!
2011-09-26
13:11:57.610 TVSports[4541:ef03] Did recieve the it!
2011-09-26 13:11:57.613 TVSports[4541:ef03] Need to fetch
if.!
2011-09-26 13:11:57.616 TVSports[4541:ef03] Need to
fetch the it.!
2011-09-26 13:11:57.618 TVSports[4541:ef03]
Need to fetch the it.!
2011-09-26 13:11:57.621
TVSports[4541:ef03] Need to fetch the it.!
2011-09-26
13:11:57.624 TVSports[4541:ef03] Need to fetch the it.!
2011-09-26 13:11:57.629 TVSports[4541:ef03] Need to fetch the
it.!
2011-09-26 13:11:57.633 TVSports[4541:ef03] Need to
fetch the it.!
2011-09-26 13:11:57.663 TVSports[4541:ef03]
Need to fetch the it.!
2011-09-26 13:11:57.669
TVSports[4541:ef03] Need to fetch the it.!
2011-09-26
13:11:57.846 TVSports[4541:ef03] -[UIDeviceWhiteColor
channelImageFetched:]: unrecognized selector sent to instance
0x65a1e30
2011-09-26 13:11:57.847 TVSports[4541:ef03] *
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[UIDeviceWhiteColor
channelImageFetched:]: unrecognized selector sent to instance
0x65a1e30'
*
First throw call stack:
(0x15da272 0x1769ce6 0x15dbf0d
0x1540e2f 0x1540c12 0x15dc092 0x3adf8 0x15dc092 0x24c43 0x15dc092
0xef9fac 0x15aec0f 0x15118c3 0x15111a4 0x1510b04 0x1510a1b 0x1ce6f67
0x1ce702c 0x608cf2 0x2718 0x2675 0x1)
terminate called throwing
an exception(gdb)

Or I get an EXC_BAD_ACCESS on a row in ASIHTTPRequest (which I can't reproduce the last 10 tries.)

Can anybody seem what I am doing wrong? Where is my code all screwed up?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

溺渁∝ 2024-12-13 06:52:49

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

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

。一般来说,当一个对象被传递给一个委托对象时,它应该保留它,直到所有可能的委托方法都被调用,此时第一个对象应该释放该委托对象,但当然它是你的。在将委托对象设置为委托之前,以及在调用所有委托方法之后(如果需要),有责任保留委托对象

[顺便说一句,虽然评论和诊断消息中的一些幽默总是受欢迎的,但使用它是相当不专业的”。妈妈……”在任何您可能会向其他人公开的代码。]

[UIDeviceWhiteColor channelImageFetched:]: unrecognized selector sent to instance 0x65a1e30 -- What this is telling you is that the "channelImageFetched" "message" was "sent" to an object of type UIDeviceWhiteController. (Ie, UIIDeviceWhiteController ->channelImageFetched" was called.) And UIDeviceWhiteController has no method named "channelImageFetched".

While this could be a problem where you're getting your pointers mixed up, more likely the object (your object) that DOES implement "channelImageFetched" was over-released and deallocated (you can confirm this by placing an NSLog in your dealloc routine.) So your problem is most likely a common storage management bug.

Understanding storage management with delegates is a hair tricky. Generally, when an object is handed a delegate object it should retain it and keep it retained until all possible delegate methods have been called, at which point that first object should release the delegate object. But of course its your responsibility to keep the delegate object retained before setting it as a delegate and, if needed, after all delegate methods have been invoked.

[BTW, while a bit of good humor in comments and diagnostic messages is always welcome, it's rather unprofessional to use "motherf..." in any code that you might expose to others.]

不…忘初心 2024-12-13 06:52:49

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

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

最终结果:

[...]
    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];
    }
[...]

Well, I still don't know what happened.. but it seems that I have found a work around.

In mu Channel.m file I changed the content slightly. Instead of using the NetworkQueue I am now just doing a startAsynchronous on the request object.

The end result:

[...]
    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];
    }
[...]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文