我有一个SurroundViewController(CollectionView),它显示从网络服务器加载的图像。如果单击图像,您将导航到DetailViewController(TableView),它会向图像显示其他信息。在NavigationController中都有Emebded(参见故事板图片)。
从DetailViewController返回时,我在SurroundViewController中进行刷新时出现问题。然后它在EXC_BAD_ACCESS
行
performSelector
崩溃
-(void)getSurroundStream {
NSString *URLString = [NSString stringWithFormat:@"%@/%@/view/%f/%f", kApiHost, kApiPath, self.sshare.coordinate.longitude, self.sshare.coordinate.latitude];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[self setAuthHeader:manager];
[manager GET:URLString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
[self.sshare putViData:responseObject];
[self.delegate performSelector:@selector(didLoadFoo)]; // --> EXC_BAD_ACCESS
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[self.error vError:error message:operation.responseString url:URLString];
}];
}
我检查了调试控制台:
2014-03-11 14:22:51.989 Foo[6923:60b] -[SurroundViewController refresh:] [Line 352] refreshing
2014-03-11 14:22:51.998 Foo[6923:60b] -[WebApi getSurroundImages] [Line 393] do surround composition
(lldb) po self.delegate
[no Objective-C description available]
似乎该对象不可用。我不明白的是。我在SurroundViewController中并通过pull-to-refresh
激活刷新。所以我在Surround View上,对象应该可用......
如何修复此问题,应用程序在performSelector行中不会因EXC_BAD_ACCESS而崩溃?
以下是涉及问题的代码(必要部分):
#import <UIKit/UIKit.h>
#import "WebApi.h"
#import "DetailViewController.h"
#import "SingletonClass.h"
@interface SurroundViewController : UICollectionViewController <WebApiDelegate>
@property (nonatomic, strong) WebApi *swebapi;
@property (nonatomic, strong) SingletonClass *sshare;
@end
#import "SurroundViewController.h"
@interface SurroundViewController ()
@property (nonatomic, strong) UIRefreshControl *refresh;
@end
@implementation SurroundViewController
-(void)vinit {
self.sshare = [SingletonClass sharedInstance];
self.swebapi = [WebApi sharedInstance];
self.swebapi.delegate = self;
}
- (void)viewDidLoad
{
[self vinit];
[self.navigationController setNavigationBarHidden:YES animated:NO];
[super viewDidLoad];
[self addRefresh];
[self.swebapi getSurroundImages]; // will call delegate didComposition
}
- (void)viewDidAppear:(BOOL)animated
{
[self.navigationController setNavigationBarHidden:YES animated:NO];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
// cell configuration
}
-(void)addRefresh {
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:@selector(refresh:) forControlEvents:UIControlEventValueChanged];
self.refresh = refreshControl;
[self.collectionView addSubview:self.refresh];
}
-(void)refresh:(UIRefreshControl*)refresh {
refresh.attributedTitle = [[NSAttributedString alloc] initWithString:@"Refreshing..."];
[self.swebapi getSurroundImages];
}
-(void)didLoadFoo {
[self.swebapi doComposition];
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
[self performSegueWithIdentifier:@"toDetailView" sender:indexPath];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:@"toDetailView"]) {
DetailViewController *dvc = [segue destinationViewController];
NSIndexPath *indexPath = sender;
dvc.idx = [self getItemOfSection:indexPath];
dvc.detailData = [[self.sshare coItem:dvc.idx] mutableCopy];
}
}
- (int)getItemOfSection:(NSIndexPath *)indexPath {
return (int)indexPath.item + ((int)indexPath.section * 4);
}
@end
#import "AFHTTPRequestOperationManager.h"
#import "Errors.h"
@class WebApi;
@protocol WebApiDelegate <NSObject>
@optional
-(void)didLoadFoo;
@end
@interface WebApi : AFHTTPRequestOperationManager <SingletonDelegate>
@property (assign, nonatomic)id<WebApiDelegate> delegate;
@property (nonatomic, strong) Errors *error;
+(WebApi*)sharedInstance;
-(void)getSurroundStream;
-(void)getSurroundImages;
@end
#import "WebApi.h"
#define kApiHost @"http://sample.com"
#define kApiPath @"sample"
@implementation WebApi
-(WebApi*)initWithBaseURL:url {
self = [super init];
if (self != nil) {
self.sshare = [SingletonClass sharedInstance];
self.error = [[Errors alloc] init];
}
return self;
}
+(WebApi*)sharedInstance
{
static WebApi *sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
sharedInstance = [[self alloc] initWithBaseURL:[NSURL URLWithString:kApiHost]];
});
return sharedInstance;
}
-(void)getSurroundStream {
NSString *URLString = [NSString stringWithFormat:@"%@/%@/view/%f/%f", kApiHost, kApiPath, self.sshare.coordinate.longitude, self.sshare.coordinate.latitude];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[self setAuthHeader:manager];
[manager GET:URLString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
[self.sshare putViData:responseObject];
[self.delegate performSelector:@selector(didLoadFoo)]; // --> EXC_BAD_ACCESS
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[self.error vError:error message:operation.responseString url:URLString];
}];
}
-(void)getSurroundImages {
[self getSurroundStream];
}
@end
#import <Foundation/Foundation.h>
@class Singleton;
@protocol SingletonDelegate <NSObject>
-(void)didRefreshToken;
@end
@interface SingletonClass : NSObject
@property (assign, nonatomic) id<SingletonDelegate> delegate;
@property (nonatomic, strong) NSMutableArray *viData;
@property (nonatomic, strong) NSMutableArray *coData;
@end
#import "SingletonClass.h"
@implementation SingletonClass
static SingletonClass *sharedInstance = nil;
// Get the shared instance and create it if necessary.
+ (SingletonClass *)sharedInstance {
if (sharedInstance == nil) {
sharedInstance = [[super allocWithZone:NULL] init];
}
return sharedInstance;
}
- (id)init
{
self = [super init];
if (self) {
self.coData = [[NSMutableArray alloc] init];
self.viData = [[NSMutableArray alloc] init];
}
return self;
}
// We don't want to allocate a new instance, so return the current one.
+ (id)allocWithZone:(NSZone*)zone {
return [self sharedInstance];
}
// Equally, we don't want to generate multiple copies of the singleton.
- (id)copyWithZone:(NSZone *)zone {
return self;
}
-(NSMutableDictionary *)coItem:(int)position {
NSAssert(self.coData.count > position, @"Position does not exists: coData.count: %lu > position: %d", (unsigned long)self.coData.count, position);
return self.coData[position];
}
@end
#import <UIKit/UIKit.h>
#import "SingletonClass.h"
#import "WebApi.h"
@interface DetailViewController : UITableViewController <WebApiDelegate>
@property (nonatomic) int idx;
@property (nonatomic, strong) SingletonClass *sshare;
@property (nonatomic, strong) WebApi *swebapi;
@property (nonatomic, strong) NSMutableDictionary *detailData;
@end
#import "DetailViewController.h"
@interface DetailViewController ()
@property (nonatomic, strong) NSArray *cellRows;
@end
@implementation DetailViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)vinit {
self.sshare = [SingletonClass sharedInstance];
self.swebapi = [WebApi sharedInstance];
self.swebapi.delegate = self;
NSAssert(self.detailData, @"detailData is not available");
}
- (void)viewDidLoad
{
[self vinit];
[self.navigationController setNavigationBarHidden:NO animated:NO];
[super viewDidLoad];
self.cellRows = @[@"cellLocation:", @"cellIntention:"];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.cellRows.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"detailCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
SEL functionCall = NSSelectorFromString(self.cellRows[indexPath.row]);
[self performSelector:functionCall withObject:cell];
return cell;
}
- (void)cellLocation:(UITableViewCell*)cell {
// configuration of table cell
}
- (void)cellIntention:(UITableViewCell*)cell {
// configuration of table cell
}
@end
答案 0 :(得分:2)
您正在将DetailViewController设置为委托。当然,在取消分配后,您将获得EXC_BAD_ACCESS。
您应该使用通知代替委托共享实例。
- (void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender
和- (void)removeObserver:(id)notificationObserver
是您的朋友。
答案 1 :(得分:1)
在您的协议中,您将didLoadDoo设置为可选,
@protocol WebApiDelegate <NSObject>
@optional
-(void)didLoadFoo;
@end
因此您需要在委托中保护对该方法的调用
if ([self.delegate respondsToSelector:@selector(didLoadFoo)]) {
[self.delegate performSelector:@selector(didLoadFoo)];
}
当您使用单身人士时
+(WebApi*)sharedInstance
如果你的singleton.delegate在你的代码中的其他地方(即你的detailVC中)发生变化,那么每个地方都会发生变化!
编辑:
经过多次检查后,现在我们知道WebApi.delegate是detailVC的变化,当我们从detailVC回来时会出现bug,因为在这一步中detailVC变为nil,当然还有WebApi.delegate。 因此,解决方案是在我们回到SurroundViewController时重置WebApi.delegate,我们可以这样做:
SurroundViewController.m
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.swebapi.delegate = self;
}