I have a bewildering problem, hoping someone can assist:
I have a model object, called Road. Here's the interface.
###
#interface RoadModel : NSObject {
NSString *_id;
NSString *roadmapID;
NSString *routeID;
NSString *title;
NSString *description;
NSNumber *collapsed;
NSNumber *isRoute;
NSString *staff;
NSNumber *start;
NSArray *staffList;
NSMutableArray *updates;
NSMutableArray *uploads;
NSMutableArray *subRoads;
}
#property (nonatomic, copy) NSString *_id;
#property (nonatomic, copy) NSString *roadmapID;
#property (nonatomic, copy) NSString *routeID;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *description;
#property (nonatomic, copy) NSNumber *collapsed;
#property (nonatomic, copy) NSNumber *isRoute;
#property (nonatomic, copy) NSString *staff;
#property (nonatomic, copy) NSNumber *start;
#property (nonatomic, copy) NSArray *staffList;
#property (nonatomic, copy) NSMutableArray *updates;
#property (nonatomic, copy) NSMutableArray *uploads;
#property (nonatomic, copy) NSMutableArray *subRoads;
- (id)initWithJSONObject:(NSDictionary *)JSONObject;
#end
This part is fine.
To give you some background, I'm translating a bunch of JSON into a proper model object so it's easier to work with.
Now, I'm trying to display this in an NSOutlineView. This is where the problem is. In particular, I have created the table and a datasource.
- (id)initWithRoads:(NSArray *)roads {
if (self = [super init])
root = [[NSMutableArray alloc] initWithArray:roads];
return self;
}
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
if (item == nil)
return root.count;
return 0;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
return NO;
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
if (item == nil)
item = root;
if (item == root)
return [root objectAtIndex:index];
return nil;
}
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
return [item title];
}
In the final datasource method, it attempts to return the "title" string property of the model object, but for some reason crashes each time. I have checked that the method is taking in the correct object (I checked [item class] description], and it is the right object), but for some reason if I call any of the objects accessors the app immediately crashes.
This is totally puzzling because in the init method, I can iterate through root (an array of RoadModel objects), and print any of its properties without issue. It is only when I'm trying to access the properties in any of the datasource methods that this occurs. I wonder if there is something memory-wise that is going on behind the scenes and I am not providing for it.
If you can shed some light on to this situation, it would be greatly appreciated!
Thanks in advance!
Usually, this kind of thing is caused by over-releasing of objects. By the time you get to the method that crashes, either your data source or your root array has been deallocated. Don't forget that NSOutlineView maintains a weak reference to its data source. This means that in reference counted world it does not retain the data source and in GC world, the reference is not enough to stop the data source from being collected.
You need to maintain a retained/strong reference elsewhere.
Related
I'm relatively new to ARC. I'm making an UIView subclass, that will have two labels (title and subtitle). I don't want to publicly expose the labels as properties, only their text.
I'm currently using this:
#interface MyView : UIView
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) NSString *subtitle;
#end
#implementation MyView
{
UILabel *_titleLabel;
UILabel *_subtitleLabel;
}
- (void)setTitle:(NSString *)title
{
[_titleLabel setText:title];
}
- (NSString *)title
{
return [_titleLabel text];
}
- (void)setSubtitle:(NSString *)subtitle
{
[_subtitleLabel setText:title];
}
- (NSString *)subtitle
{
return [_subtitleLabel text];
}
#end
Are my two #properties correctly declared? Should I use the strong, weak or any other qualifier? And why?
If you are going to work with setter / getter, I think the appropiate tag would be the readwrite. strong weak retain etc apply when the property is the setter/getter for an instance variable.
I have a iPad app, using XCode 4.5, Storyboards, Core Data and iOS 6. I select a row, make a change to the contents of the record (which is successful), but the row doesn't change. I have tried to refresh the UITableView, but cellForRowAtIndexPath is never called. I have searched SO and Google to no avail; I don't see what's wrong. Can someone please tell me how to fix this? (with an explanation of what I'm doing wrong for the next time?)
Here is the pertinent code:
- (IBAction)btnModify:(UIButton *)sender {
//NSLog(#"btnModify clicked");
NSManagedObjectContext *localContext = [NSManagedObjectContext MR_contextForCurrentThread];
// find client by primary telephone number
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"aClientPrimaryPhone ==[c] %#", cvPrimaryPhone.text];
ClientInfo *clientDataFound = [ClientInfo MR_findFirstWithPredicate:predicate inContext:localContext];
if(clientDataFound) {
clientDataFound.aClientName = cvCustName.text; // now start moving the data
clientDataFound.aClientAddr1 = cvAddress1.text;
clientDataFound.aClientAddr2 = cvAddress2.text;
clientDataFound.aClientCity = cvContactCity.text;
clientDataFound.aClientPostalCode = cvPostalCode.text;
clientDataFound.aClientCellPhone = cvCellPhone.text;
clientDataFound.aClientPrimaryPhone = cvPrimaryPhone.text;
clientDataFound.aClientEMail = cvPersonalEmail.text;
clientDataFound.aClientNotes = cvNotes.text;
[localContext MR_saveNestedContexts];
[self reloadClientList];
}
}
-(void) reloadClientList {
//Init Array to hold TableView Data
tableDataArray = [NSMutableArray new];
[tableDataArray addObjectsFromArray:[ClientInfo findAll]]; // Load
[self.clientList reloadData];
}
and this is ClientInfo.m
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface ClientInfo : NSManagedObject
#property (nonatomic, retain) NSString * aClientAddr1;
#property (nonatomic, retain) NSString * aClientAddr2;
#property (nonatomic, retain) NSString * aClientCellPhone;
#property (nonatomic, retain) NSString * aClientCity;
#property (nonatomic, retain) NSString * aClientEMail;
#property (nonatomic, retain) NSData * aClientImage;
#property (nonatomic, retain) NSString * aClientName;
#property (nonatomic, retain) NSString * aClientNotes;
#property (nonatomic, retain) NSString * aClientPostalCode;
#property (nonatomic, retain) NSString * aClientPrimaryPhone;
#end
I found it... my "clientList" was NOT connected to the object... don't know how I missed that one!
There are a few reasons I can think of:
Your table view reference clientList is nil. (not connected)
Your table view's DataSource & Delegate is not set (Actually I'm not sure if it compiles when DataSource is not set)
Your table view is a subclass of UITableView and in that subclass reloadData method is overridden.
I have a subclass of UITableViewController, and I init the subclass with a NSMutableArray of another custom class:
#import <UIKit/UIKit.h>
#import "NUBCheckpointModel.h"
#interface NUBUserCheckpointModel : NSObject
#property (nonatomic,assign) NSString* objId;
#property (nonatomic,assign) NSString* userId;
#property (nonatomic,assign) NSString* checkpointId;
#property (nonatomic,assign) NSDate* dateAdded;
#property (nonatomic,assign) NUBCheckpointModel* checkpoint;
+ (NUBUserCheckpointModel*) fromJson: (NSString*)json;
#end
This array that is generated from another ViewController, gets passed into this subclassed TableViewController, of which contain this property
#property (nonatomic,retain) NSMutableArray* userCheckpointData;
This property is set like this:
- (id)initWithFrame: (CGRect)frame withType: (TableType)typeOfTable fromParent: (UIViewController*)parent data: (NSMutableArray*)ucpData
{
self = [self init];
if (self) {
self.tableView = [[UITableView alloc] initWithFrame:frame];
self.parentController = parent;
self.userCheckpointData = ucpData;
[self styleTable];
[self addPullToRefreshHeader];
typeCategory = typeOfTable;
}
return self;
}
All is fine up to this part, and any manipulation including trying to get an object from the array works fine. I tested it.
The code I used to test the array is:
NUBUserCheckpointModel* model = [self.userCheckpointData objectAtIndex:0];
NSLog(model.objId);
However, this very same code, when used here:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
Gives me exc_bad_access. May I know why this happens? I can't seem to figure out why. I'm using ARC btw. Thank you.
While adding the property, you need to take care of the memory management. For string, it is not good practice to set assign property.
Instead , do as following,
#property (nonatomic,copy) NSString* objId;
#property (nonatomic,copy) NSString* userId;
#property (nonatomic,copy) NSString* checkpointId;
#property (nonatomic,retain) NSDate* dateAdded;
#property (nonatomic,retain) NUBCheckpointModel* checkpoint;
I have 5 NSMutableArrays in cell. I need to sort cells by one value.
Example I need to sort cell by time.
[MyArray1 sortUsingSelector:#selector(compare:)];
but how I will be with other 4 NSMutableArray in cell?
It's not a good idea to store data for your cells in 5 arrays, don't separate them; create a data container class, store all values for each cell inside one data container object and then you can sort your array with data containers by one of the values.
e.g.:
DataContainer.h:
#interface DataContainer : NSObject
{
NSDate *date1;
NSDate *date2;
NSString *upperTitle;
NSString *mainTitle;
NSString *subtitle;
}
#property (nonatomic, strong) NSDate *date1;
#property (nonatomic, strong) NSDate *date2;
#property (nonatomic, strong) NSString *upperTitle;
#property (nonatomic, strong) NSString *mainTitle;
#property (nonatomic, strong) NSString *subtitle;
#end
DataContainer.m:
#implementation DataContainer
#synthesize date1, date2, upperTitle, mainTitle, subtitle;
#end
Then you can create your DataContainer's (one for each cell) and store them in one NSMutableArray.
e.g.:
DataContainer *container = [[DataContainer alloc] init];
[container setDate1:[NSDate date]];
[container setMainTitle:#"blahblah"];
///...
[cellArr addObject:container];
To sort this array, use:
cellArr = [cellArr sortedArrayUsingComparator:^(id cont1, id cont2) {
return [[(DataContainer *) cont1 date1] compare:[(DataContainer *) cont2 date1]];
}];
and then use them in your cellForRowAtIndexPath:
DataContainer *container = [cellArr objectAtIndex:indexPath.row];
//container.date1, container.date2, container.upperTitle, container.mainTitle and container.subtitle are the values that you need for your cell.
Notice that this code is suitable if you're using ARC (Automatic Reference Counting) for your project; if you're not using ARC, then you need to change strong to retain in property definitions and add release's to needed places to avoid memory leaks.
I agree with Andrey. You stick everything from your arrays in containers and then you sort the containers.
Your container should have as members everything you store in one cell (date, time, text, text2, Englis/mathematic, etc).
And then you sort the cell container array.
cellArr = [cellArr sortedArrayUsingComparator:^(id cont1, id cont2) {
// if date in container 1 is earlier than in container 2
return (NSComparisonResult)NSOrderedDescending;
// if date is later
return (NSComparisonResult)NSOrderedAscending;
// if none of the above
return (NSComparisonResult)NSOrderedSame;
}];
UPDATE:
I found that the reason for the previous error was an error in the documentation.
The method should be named proxyForJson, not jsonProxyObject...
But I'm still stuck, though.
I now get an EXC_BAD_ACCESS error inside stringWithObject some where. Any clues?
UPDATE 2:
My proxyForJson implementation is a cut-n-paste from then documentation:
- (id)proxyForJson {
return [NSDictionary dictionaryWithObjectsAndKeys:
Navn, #"Navn",
Adresse, #"Adresse",
Alder, #"Alder",
nil];
}
Trying to make json serialization work for my custom objective-c class.
As I understand the documentation, json-framework can serialize custom objects, if they implement the jsonProxyObject method.
So I have this class:
#interface MyObject : NSObject {
NSString *Name;
NSString *Addresse;
NSInteger Age;
}
#property (nonatomic, retain) NSString *Name;
#property (nonatomic, retain) NSString *Addresse;
#property (nonatomic, assign) NSInteger Age;
- (id)jsonProxyObject;
#end
And I try to serialize an array with some instances in it:
[json stringWithObject:list error:&error];
But all I get is he following error:
"JSON serialisation not supported for MyObject"
I guess the jsonWriter can't find my jsonProxyObject method for some reason, buy why?
Regards.
Have you tried to turn on NSZombies and MallocStackLogging in your Executable Info pane to check the source of the EXC_BAD_ACCESS? If not, you might try this and check the console for the output.
EXC_BAD_ACCESS is often an error caused by over-releasing an object somewhere.
I am not sure whether this is the right thing to do but defining the class as follows, solves the problem:
#interface MyObject : NSObject {
NSString *Name;
NSString *Addresse;
NSInteger *Age;
}
#property (nonatomic, retain) NSString *Name;
#property (nonatomic, retain) NSString *Addresse;
#property (nonatomic, retain) NSInteger *Age;
- (id)jsonProxyObject;
#end
Then initializing the variable as:
Age = [[NSNumber alloc] initWithInt:32];
In NSString, there is no stringWithObject: method. You should try using stringWithFormat: instead.