I can't figure out why I am unable to set the value of an instance of PFObject. It is working successfully elsewhere in the app, but I must be doing something wrong in this view. I'd really appreciate your help figuring out what I'm missing. Thanks.
Basically
self.ABC.type = #"Frustrated";
NSLog(#"why is this null? -----> %#",self.ABC.type);
2015-10-18 18:26:29.277 XX [885:109003] why is this null? -----> (null)
Why is the value not being assigned? It should log as ... I'm not null or frustrating... I'm working, see -----> Frustrated !!!!!! but it is not working!
I'm therefore unable to set the object in Parse bc it's nil and the app crashes.
Below should be all and more of the code you need, but let me know it you have questions or suggestions. Thanks!:
FCollectionViewController.m:
#import "FCollectionViewController.h"
#import "FCell.h"
#import "SectionHeaderView.h"
#import "MBProgressHUD.h"
#import "Helper.h"
#interface FCollectionViewController()
<UICollectionViewDelegate, MBProgressHUDDelegate>
#property (nonatomic, strong) NSDictionary *presetsDictionary;
#property (nonatomic, strong) NSArray *presets;
#property (nonatomic, strong) NSIndexPath *textViewIndexPath;
#property (nonatomic, strong) FCell *fCell;
#property (nonatomic, strong) MBProgressHUD *HUD;
#property (nonatomic, strong) NSArray *headTitleArray;
#end
#implementation FCollectionViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.headTitleArray = [NSArray arrayWithObjects:
#"type",
#"model",
#"feature", nil];
PFQuery *query = [PFQuery queryWithClassName:#"Presets"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
NSMutableArray *keys = [[NSMutableArray alloc] init];
NSMutableArray *values = [[NSMutableArray alloc] init];
for (PFObject *obj in objects) {
[keys addObject:[obj objectForKey:#"key"]];
[values addObject:[obj objectForKey:#"value"]];
}
self.presetsDictionary = [NSDictionary dictionaryWithObjects:values forKeys:keys];
self.presets = [[NSMutableArray alloc] initWithObjects:
[self.presetsDictionary objectForKey:#"type"],
[self.presetsDictionary objectForKey:#"model"],
[self.presetsDictionary objectForKey:#"feature"],nil];
NSLog(#"self.presets ----> %#", self.presets);
[self.collectionView reloadData];
}];
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return [self.presets count];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return [self.presets[section] count];
}
- (UICollectionReusableView*)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
UICollectionReusableView * view = nil;
if ([kind isEqualToString:UICollectionElementKindSectionHeader])
{
ItemSectionHeaderView *header = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:NSStringFromClass([ItemSectionHeaderView class])
forIndexPath:indexPath];
header.captionLabel.text = self.headTitleArray[indexPath.section];
view = header;
}
return view;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell;
FCell *aCell = (FCell *)[collectionView dequeueReusableCellWithReuseIdentifier:#"fCell" forIndexPath:indexPath];
aCell.label.text = self.presets[indexPath.section][indexPath.row];
aCell.label.textAlignment = NSTextAlignmentCenter;
cell = aCell;
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
CGSize size;
....
return size;
}
#pragma mark <UICollectionViewDelegate>
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSArray * selectedRows = self.collectionView.indexPathsForSelectedItems;
for (NSIndexPath * selectedRow in selectedRows) {
if ((selectedRow.section == indexPath.section) && (selectedRow.row != indexPath.row)) {
[self.collectionView deselectItemAtIndexPath:selectedRow animated:NO];
}
}
switch (indexPath.section) {
case 0:
self.aBC.type = #"Frustrated";
NSLog(#"why isn't this being assigned? -----> %#",self.ABC.type);
break;
case 1:
self.aBC.model = self.presets[indexPath.section][indexPath.row];
NSLog(#"why is this null?!!!! -----> %#",self.ABC.model);
NSLog(#"this DOES log the value I want!! -----> %#",self.presets[indexPath.section][indexPath.row]);
break;
case 2:
...
default:
break;
}
}
FCollectionViewController.h:
#import <UIKit/UIKit.h>
#import "ABC.h"
#interface FCollectionViewController : UICollectionViewController
//<UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
#property (nonatomic, strong) ABC *aBC;
#end
ABC.h:
#import <Foundation/Foundation.h>
#interface ABC : NSObject
#property (nonatomic, strong) NSString *type;
#property (nonatomic, strong) NSString *model;
#property (nonatomic, strong) NSString *feature;
#property (nonatomic, strong) PFObject *pfObj;
#property (nonatomic, strong) PFUser *user;
- (id)initWithPFObject:(PFObject *)anObject;
#end
ABC.m:
#import "ABC.h"
#implementation ABC
- (id)initWithPFObject:(PFObject *)anObject
{
if(self = [super init])
{
[anObject fetchIfNeeded];
self.pfObj = anObject;
self.type = [anObject objectForKey:#"type"];
self.model = [anObject objectForKey:#"model"];
...
}
return self;
}
#end
FCell.h:
#import <UIKit/UIKit.h>
#interface FCell : UICollectionViewCell
#property (weak, nonatomic) IBOutlet UILabel *label;
#end
FCell.m:
#import "FCell.h"
#implementation FCell
-(void)setHighlighted:(BOOL)highlighted
{
[super setHighlighted:highlighted];
self.selectedBackgroundView = [[UIView alloc] initWithFrame:self.frame];
self.selectedBackgroundView.backgroundColor = [UIColor darkGrayColor];
[self setNeedsDisplay];
}
#end
aBC isn't initialized anywhere. You declare it, but it isn't initialized. So setting aBC.type isn't actually setting anything.
Add self.aBC = [[ABC alloc] init]; in your viewDidLoad method.
Related
I have project with normal cell and working success but I want to change it with custom cell I added Cell.h and Cell.m files into my project and I need to integrate custom cell to my working cell. And last I want to show detail view title description and image (I added title codes working) My codes under
Cell.h
#import <UIKit/UIKit.h>
#interface Cell : UITableViewCell
#property (nonatomic, weak) IBOutlet UIImageView *imaj;
#property (nonatomic, weak) IBOutlet UILabel *descriptionLabel;
#property (nonatomic, weak) IBOutlet UILabel *titleLabel;
#end
Cell.m
#import "Cell.h"
#implementation Cell
#synthesize imaj = _imaj;
#synthesize descriptionLabel = _descriptionLabel;
#synthesize titleLabel = _titleLabel;
#end
ViewController.m
#import "MasterViewController.h"
#import "DetailViewController.h"
#import "SDWebImage/UIImageView+WebCache.h"
#import "MBProgressHUD.h"
#import "Cell.h"
static NSString *const kConsumerKey = #"a1SNULSPtp4eLQTsTXKKSgXkYB5H4CMFXmleFvqE";
#interface MasterViewController () <UISearchBarDelegate, UISearchDisplayDelegate,MBProgressHUDDelegate>{
MBProgressHUD *HUD;
}
#property (nonatomic, assign) NSInteger currentPage;
#property (nonatomic, assign) NSInteger totalPages;
#property (nonatomic, assign) NSInteger totalItems;
#property (nonatomic, assign) NSInteger maxPages;
#property (nonatomic, strong) NSMutableArray *activePhotos;
#property (strong, nonatomic) NSMutableArray *staticDataSource;
#property (nonatomic, strong) NSMutableArray *searchResults;
#property (strong, nonatomic) IBOutlet UISearchBar *searchBar;
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#end
#implementation MasterViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.activePhotos = [[NSMutableArray alloc] init];
self.searchResults = [[NSMutableArray alloc] init];
self.staticDataSource = [[NSMutableArray alloc] init];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self loadPhotos:self.currentPage];
}
#pragma mark - Table View
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// if (self.currentPage == self.maxPages
// || self.currentPage == self.totalPages
// || self.currentPage == self.totalPages
// || self.totalItems == self.photos.count) {
// return self.photos.count;
// } else if (self.tableView == self.searchDisplayController.searchResultsTableView){
// return [self.searchResults count];
//
// }
// return self.photos.count + 1;
return self.activePhotos.count + 1;
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.currentPage != self.maxPages && indexPath.row == [self.staticDataSource count] - 1 ) {
[self loadPhotos:++self.currentPage];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
if (indexPath.row == [self.activePhotos count]) {
cell = [self.tableView dequeueReusableCellWithIdentifier:#"LoadingCell" forIndexPath:indexPath];
HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:HUD];
HUD.delegate = self;
HUD.labelText = #"Loading";
[HUD showWhileExecuting:#selector(myTask) onTarget:self withObject:nil animated:YES];
} else {
NSDictionary *photoItem = self.activePhotos[indexPath.row];
cell = [self.tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
// cell.textLabel.text = [self.searchResults[indexPath.row] valueForKey:#"name"];
// } else {
// NSDictionary *photoItem = self.photos[indexPath.row];
cell.textLabel.text = [photoItem objectForKey:#"name"];
if (![[photoItem objectForKey:#"description"] isEqual:[NSNull null]]) {
cell.detailTextLabel.text = [photoItem objectForKey:#"description"];
}
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:[photoItem objectForKey:#"image_url"] ] placeholderImage:[UIImage imageNamed:#"placeholder.jpg"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
if (error) {
NSLog(#"Error occured : %#", [error description]);
}
}];
}
// NSLog(#"%#",self.searchResults);
return cell;
}
- (void)myTask {
// Do something usefull in here instead of sleeping ...
sleep(1.5);
}
#pragma mark UISearchDisplay delegate
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
// [self.searchResults removeAllObjects];
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"SELF.name contains[c] %#", searchText];
self.activePhotos = [NSMutableArray arrayWithArray:[self.staticDataSource filteredArrayUsingPredicate:resultPredicate]];
//[self.tableData filteredArrayUsingPredicate:resultPredicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
- (void)loadPhotos:(NSInteger)page
{
NSString *apiURL = [NSString stringWithFormat:#"https://api.500px.com/v1/photos?feature=editors&page=%ld&consumer_key=%#",(long)page,kConsumerKey];
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:apiURL]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
if (!error) {
NSError *jsonError = nil;
NSMutableDictionary *jsonObject = (NSMutableDictionary *)[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&jsonError];
NSLog(#"%#",jsonObject);
[self.staticDataSource addObjectsFromArray:[jsonObject objectForKey:#"photos"]];
self.currentPage = [[jsonObject objectForKey:#"current_page"] integerValue];
self.totalPages = [[jsonObject objectForKey:#"total_pages"] integerValue];
self.totalItems = [[jsonObject objectForKey:#"total_items"] integerValue];
self.activePhotos = self.staticDataSource;
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
}] resume];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
DetailViewController *vc = segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
vc.StoreList = [self.activePhotos objectAtIndex:indexPath.row];
}
#end
Also I uploaded working project here
http://www.filedropper.com/needcustomcell
In your
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Cell *cell;
// rest of your code
}
that would do it. You have used UITableViewCell you need to replace ot with your custom cell class.
I'm taking over some code since another dev has moved on and his search bar has an error. I've searched the other threads for help on this, but none of them seem to be my error. Here is a screen shot of mine and the code.
http://imgur.com/gO6wuPb
2014-09-17 11:25:34.665 Moca[11388:60b] * Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ valueForUndefinedKey:]: this class is not key value coding-compliant for the key itemName.'
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [searchResults count];
} else {
return [[[BNRItemStore sharedStore] allItems] count];
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 71;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"UITableViewCell";
UITableViewCell *cell = (UITableViewCell *)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//there are two tableviews involved here - one is the main one, and one is associated with searchDisplayController
//self.tableView is set automatically between the main one and the search one.
// Configure the cell...
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSArray *items = [[BNRItemStore sharedStore] allItems];
BNRItem *item = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
item = [searchResults objectAtIndex:indexPath.row];
} else {
item = items[indexPath.row];
}
if ([item.subcategory isEqual:#"Urgent"]) {
cell.imageView.image=[UIImage imageNamed:#"Urgent.png"];
}
else if ([item.subcategory isEqual:#"Emergency"]) {
cell.imageView.image=[UIImage imageNamed:#"Emergency.png"];
}
else if ([item.subcategory isEqual:#"Standard"]) {
cell.imageView.image=[UIImage imageNamed:#"Standard.png"];
}
else if ([item.subcategory isEqual:#"Normal"]) {
cell.imageView.image=[UIImage imageNamed:#"Normal.png"];
}
else {
cell.imageView.image=[UIImage imageNamed:#"Informational.png"];
}
cell.textLabel.text = item.changeOrder;
cell.detailTextLabel.text =item.title;
return cell;
}
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSArray *items = [[BNRItemStore sharedStore] allItems];
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"itemName contains[c] %#", searchText];
searchResults = [items filteredArrayUsingPredicate:resultPredicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
EDIT add item details
#import <Foundation/Foundation.h>
#interface BDDetailedChange : NSObject
+ (instancetype)randomItem;
- (instancetype)initWithItemName:(NSString *)name
valueInDollars:(NSString *)value
serialNumber:(NSString *)sNumber;
#property (nonatomic, copy) NSString *itemName;
#property (nonatomic, copy) NSString *serialNumber;
#property (nonatomic, copy) NSString *valueInDollars;
#property (nonatomic, readonly, strong) NSDate *dateCreated;
#property (nonatomic, copy) NSString *subcategory; //Emergency
#property (nonatomic, copy) NSString *service;
#property (nonatomic, copy) NSString *associatedCIs;
#property (nonatomic, copy) NSString *environment;// Production
#property (nonatomic, copy) NSString *approvalGroup;
#property (nonatomic, copy) NSString *initiator;
#property (nonatomic, copy) NSString *coordinator;
#property (nonatomic, copy) NSString *riskLevel;// Low
#property (nonatomic, copy) NSString *performingGroup;
#property (nonatomic, copy) NSString *implementationPlan;
#property (nonatomic, copy) NSString *validationPlan;
#property (nonatomic, copy) NSString *recoveryScope;
#end
BNR item .h
#interface BNRItem : NSObject
{
NSString *_approverEid;
NSString *_assignmentGroup;
NSString *_changeOrder;
NSString *_subcategory; //Emergency
NSString *_title; //title
}
- (void)setApproverEid: ( NSString *)aEid;
- (NSString*)approverEid;
- (void)setAssignmentGroup: ( NSString *)aGrp;
- (NSString*)assignmentGroup;
- (void)setChangeOrder: ( NSString *)co;
- (NSString*)changeOrder;
- (void)setSubcategory: ( NSString *)sub;
- (NSString*)subcategory;
- (void)setTitle: ( NSString *)title;
- (NSString*)title;
#end
BNRItem.m
#import "BNRItem.h"
#implementation BNRItem
- (void)setApproverEid:(NSString *)aEid
{
_approverEid = aEid;
}
- (void)setAssignmentGroup: ( NSString *)aGrp
{
_assignmentGroup = aGrp;
}
- (void)setChangeOrder: ( NSString *)co
{
_changeOrder=co;
}
- (void)setSubcategory: ( NSString *)sub
{
_subcategory= sub;
}
- (void)setTitle: ( NSString *)title
{
_title = title;
}
- (NSString*)approverEid
{
return _approverEid;
}
- (NSString*)assignmentGroup
{
return _assignmentGroup;
}
- (NSString*)changeOrder
{
return _changeOrder;
}
- (NSString*)subcategory
{
return _subcategory;
}
- (NSString*)title
{
return _title;
}
#end
I've programmatically created a UITableView within my MatchCenterViewController, however it doesn't seem to populate with the JSON data being returned by my cloud code function. It simply shows a blank View Controller. MatchCenterViewController is a ViewController embedded within a Navigation View Controller.
MatchCenterViewController.h:
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#import "AsyncImageView.h"
#import "SearchViewController.h"
#interface MatchCenterViewController : UIViewController <UITableViewDataSource>
#property (nonatomic) IBOutlet NSString *itemSearch;
#property (nonatomic, strong) NSArray *imageURLs;
#property (strong, nonatomic) NSString *matchingCategoryCondition;
#property (strong, nonatomic) NSString *matchingCategoryLocation;
#property (strong, nonatomic) NSNumber *matchingCategoryMaxPrice;
#property (strong, nonatomic) NSNumber *matchingCategoryMinPrice;
#property (strong, nonatomic) NSArray *matchCenterArray;
#end
MatchCenterViewController.m:
#import "MatchCenterViewController.h"
#import <UIKit/UIKit.h>
#interface MatchCenterViewController () <UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, strong) UITableView *matchCenter;
#end
#implementation MatchCenterViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.matchCenter = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
_matchCenter.dataSource = self;
_matchCenter.delegate = self;
[self.view addSubview:self.matchCenter];
}
return self;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 3;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [_matchCenter dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSDictionary *matchCenterDictionary= [self.matchCenterArray objectAtIndex:indexPath.row];
cell.textLabel.text = [matchCenterDictionary objectForKey:#"Title"];// title of the first object
// if([matchCenterDictionary objectForKey:#"Price"] != NULL)
// {
// cell.detailTextLabel.text = [NSString stringWithFormat:#"$%#",[matchCenterDictionary objectForKey:#"Price"]];
// }
return cell;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.matchCenterArray = [[NSArray alloc] init];
}
- (void)viewDidAppear:(BOOL)animated
{
self.matchCenterArray = [[NSArray alloc] init];
[PFCloud callFunctionInBackground:#"MatchCenterTest"
withParameters:#{
#"test": #"Hi",
}
block:^(NSDictionary *result, NSError *error) {
if (!error) {
self.matchCenterArray = [result objectForKey:#"Top 3"];
dispatch_async(dispatch_get_main_queue(), ^{
[_matchCenter reloadData];
});
NSLog(#"Test Result: '%#'", result);
}
}];
}
#end
How do you create this viewController? If you don't use [[MatchCenterViewController alloc] initWithNibName:... bundle:...] the table creation won't be called. This might happen because you have the viewController in a storyBoard, in this case initWithCoder: would be the method to overwrite.
I would recommend to move this code to viewDidLoad, which will be called regardless how the viewController was created . E.g.:
- (void)viewDidLoad {
[super viewDidLoad];
self.matchCenter = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
_matchCenter.dataSource = self;
_matchCenter.delegate = self;
[self.view addSubview:self.matchCenter];
self.matchCenterArray = [[NSArray alloc] init];
}
I have a SurroundViewController (CollectionView) which shows images loaded from a webserver. If you click on the image you will be navigated to a DetailViewController (TableView) which shows additional information to the image. Both Emebded in a NavigationController (see storyboard image).
My problem start when I do a refresh in the SurroundViewController, when coming back from the DetailViewController. Then it crashes with EXC_BAD_ACCESS on the performSelector line
WebApi.m
-(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];
}];
}
I checked in the Debug Console:
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]
Seems that the object which is not available. What I do not understand is. I am in the SurroundViewController and do activly a refresh by pull-to-refresh. So I am on the Surround View and the object should be available...
How do I fix this issue, that the App does not crash with EXC_BAD_ACCESS at the performSelector line?
Here's the code which is involved with the issue (necessary parts):
SurroundViewController.h
#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
SurroundViewController.m
#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
WebApi.h
#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
WebApi.m
#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
SingletonClass.h
#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
SingletonClasss.m
#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
DetailViewController.h
#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
DetailViewController.m
#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
You are setting DetailViewController as delegate. Of course you will get EXC_BAD_ACCESS after it's deallocated.
You should use notifications instead delegates for shared instances.
- (void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender and - (void)removeObserver:(id)notificationObserver are yours friends.
In your protocol, you set didLoadDoo as optional,
#protocol WebApiDelegate <NSObject>
#optional
-(void)didLoadFoo;
#end
so you need to secure the call to that method in your delegate
if ([self.delegate respondsToSelector:#selector(didLoadFoo)]) {
[self.delegate performSelector:#selector(didLoadFoo)];
}
As you are working with a singleton
+(WebApi*)sharedInstance
if your singleton.delegate is change somewhere else in your code (i.e. in your detailVC), it is change everyWhere !
edit :
After some more check, now we know that WebApi.delegate is change in detailVC, and the bug appear when we are back from detailVC because at this step detailVC is becoming nil and of course WebApi.delegate also.
So, the solution is to reset WebApi.delegate when we are back in SurroundViewController, and we can do that in :
SurroundViewController.m
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.swebapi.delegate = self;
}
I think just showing my whole code is better than my explanation.. actually I'm not good at expressing something in English.X(
this code is .h file for .m file below.
#import <UIKit/UIKit.h>
#define kImageValueTag 0
#define kNameValueTag 1
#define kSubtitleValueTag 2
#define kMemoValueTag 3
#class PhonebookDetailedViewController;
#interface PhonebookViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
{
UITableViewCell *tvCell;
UITableView *tableView;
UISearchBar *searchBar;
NSString *fullName;
NSMutableDictionary *names, *pictures;
NSArray *keys, *sortedKeys, *sortedAllValues;
PhonebookDetailedViewController *childController;
}
#property (nonatomic, retain) IBOutlet UITableViewCell *tvCell;
#property (nonatomic, retain) IBOutlet UISearchBar *searchBar;
#property (nonatomic, retain) IBOutlet UITableView *tableView;
#property (nonatomic, retain) UIImage *phoneImage;
#property (nonatomic, retain) NSString *fullName;
#property (nonatomic, retain) NSMutableDictionary *names;
#property (nonatomic, retain) NSMutableDictionary *pictures;
#property (nonatomic, retain) NSArray *keys;
#property (nonatomic, retain) NSArray *sortedKeys;
#property (nonatomic, retain) NSArray *sortedAllValues;
#end
this code is for the .m file that implement showing a table. When I try to scroll the table down, the error named EXC_BAD_ACCESS is suddenly called at - (NSString *)tableView: titleForHeaderInSection: method. I guess sortedKeys releases at somewhere because I can do [sortedKeys count] before error, but cannot when error comes. I don't know where it releases and why it releases, however. Please show your ideas to me. Any ideas are okay. Thank you in advance.
#import "PhonebookViewController.h"
#import "PhonebookDetailedViewController.h"
#import "NSString-SortForIndex.h"
#implementation PhonebookViewController
#synthesize tvCell;
#synthesize searchBar;
#synthesize tableView;
#synthesize phoneImage;
#synthesize fullName;
#synthesize names, pictures;
#synthesize keys, sortedAllValues, sortedKeys;
//- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
//{
// self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
// if (self) {
// // Custom initialization
// }
// return self;
//}
//- (void)didReceiveMemoryWarning
//{
// // Releases the view if it doesn't have a superview.
// [super didReceiveMemoryWarning];
//
// // Release any cached data, images, etc that aren't in use.
//}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect bounds = self.tableView.bounds;
bounds.origin.y = bounds.origin.y + searchBar.bounds.size.height;
self.tableView.bounds = bounds;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *nameFilePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"nameContacts.dict"];
NSString *pictureFilePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"imageContacts.dict"];
self.names = (NSMutableDictionary *)[NSKeyedUnarchiver unarchiveObjectWithFile:nameFilePath];
self.pictures = (NSMutableDictionary *)[NSKeyedUnarchiver unarchiveObjectWithFile:pictureFilePath];
self.keys = [self.names allKeys];
sortedKeys = [self.keys sortedArrayUsingSelector:#selector(sortForIndex:)];
sortedAllValues = [[NSArray alloc] init];
for (NSString *sortedKey in sortedKeys)
{
NSArray *selectedValues = [self.names valueForKey:sortedKey];
for (NSString *selectedValue in selectedValues)
sortedAllValues = [sortedAllValues arrayByAddingObject:selectedValue];
}
// Do any additional setup after loading the view from its nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.tableView = nil;
self.searchBar = nil;
self.tvCell = nil;
self.fullName = nil;
self.names = nil;
self.pictures = nil;
self.keys = nil;
self.sortedKeys = nil;
self.sortedAllValues = nil;
}
- (void)dealloc
{
[tableView release];
[searchBar release];
[tvCell release];
[fullName release];
[names release];
[pictures release];
[keys release];
[sortedKeys release];
[sortedAllValues release];
[super dealloc];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table Data Source Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [sortedKeys count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSString *key = [sortedKeys objectAtIndex:section];
NSArray *nameSection = [names objectForKey:key];
return [nameSection count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CustomCellIdentifier = #"CustomCellIdentifier";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CustomCellIdentifier];
if ( cell == nil )
{
NSArray *nib = [[NSBundle mainBundle]
loadNibNamed:#"CustomPhonebookCell"
owner:self
options:nil];
if (nib.count > 0)
{
cell = self.tvCell;
} else
{
NSLog(#"Failed to load CustomPhonebookCell nib file!");
}
}
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
NSUInteger row = [indexPath row];
NSUInteger section = [indexPath section];
NSString *foundKey = [sortedKeys objectAtIndex:section];
NSArray *nameSection = [self.names objectForKey:foundKey];
UILabel *nameLabel = (UILabel *)[cell viewWithTag:kNameValueTag];
nameLabel.text = [nameSection objectAtIndex:row];
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSString *key = [sortedKeys objectAtIndex:section]; // EXC_BAD_ACCESS error at here
return key;
}
sortedKeys is not properly retained. When you set it use self.
self.sortedKeys = [self.keys sortedArrayUsingSelector:#selector(sortForIndex:)];