Simple class example with #property not allowing property retrieval - ios

I created a Food object that I'd like to be able to set and get attributes of (such as time). For some reason I'm allowed to set attributes, but I am not able to the get attributes. I receive the following error by simply calling food.time
Property 'time' not found on object of type 'conts __strong id'
I'm not sure if the problem is with putting it in, and then retrieving it from, an array or if it is how my object class is defined. In this example I've simplified it so that you can see how I'm using it.
Some controller (with other methods not shown here)
#import "Food.h"
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *foodArray = #[firstFood];
for (id food in foodArray) {
UILabel *foodLabel = [[UILabel alloc]
initWithFrame:CGRectMake(10, 180, self.view.frame.size.width-20, 50)];
foodLabel.backgroundColor = [UIColor clearColor];
foodLabel.text = food.time; // This line causes error
foodLabel.textColor = [UIColor blackColor];
[foodLabel setFont:[UIFont fontWithName:#"Courier" size:14]];
[self.view addSubview:foodLabel];
}
}
Food.h
#import <Foundation/Foundation.h>
#interface Food : NSObject
#property (strong, nonatomic) NSString *time;
#property (strong, nonatomic) NSString *title;
#property (strong, nonatomic) NSString *description;
#property (strong, nonatomic) NSString *place;
#end
Food.m
#import "Food.h"
#implementation Food
#end

As far as the compiler knows food is just a generic NSObject pointer. You either need to cast it to a Food object, or just change your definition within the for loop.
for (Food *food in foodArray) {
//...etc
}
That is assuming firstFood is actually a Food object, since you do not show it's definition in your snippet.
If you don't want to change the type, you can send any message to id, and let it figure out at run-time whether it is valid:
foodLabel.text = [food time];
would also be valid, but you are unable to use the dot syntax on an object of type id, either cast it or use the standard bracket syntax (which will fail at run-time if that object does not respond to that message).

Related

Accessing properties from Object within NSMutableArray not working

I have the class LearnfestItem.h :
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface LearnfestItem : NSObject
#property (strong, nonatomic) NSString * itemId;
#property (strong, nonatomic) NSString * itemTitle;
#property (strong, nonatomic) NSString * itemDescription;
#property (strong, nonatomic) NSString * itemContent;
#property (strong, nonatomic) NSString * itemType;
#property (strong, nonatomic) UIImage * itemImage;
#property (strong, nonatomic) NSDate * itemRegistered;
-(id)initWithData:(NSDictionary *)data andImage:(UIImage *)image;
#end
& Object.m :
#import "LearnfestItem.h"
#import "Defaults.h"
#implementation LearnfestItem
-(id)init
{
self = [self initWithData:nil andImage:nil];
return self;
}
-(id)initWithData:(NSDictionary *)data andImage:(UIImage *)image
{
self = [super init];
self.itemId = data[ITEM_ID];
self.itemTitle = data[ITEM_TITLE];
self.itemDescription = data[ITEM_DESCRIPTION];
self.itemContent = data[ITEM_CONTENT];
self.itemType = data[ITEM_TYPE];
self.itemImage = image;
self.itemRegistered = data[ITEM_REGISTERED];
return self;
}
#end
In my UIViewController I have a UITableView that creates a NSMutableArray of LearnfestItems within the cellForRowAtIndexPath method:
LearnfestItem * createLearnfestItem = [[LearnfestItem alloc] initWithData:learnfestItemDictionary andImage:learnfestItemImage];
NSLog(#"Insert learnfest item with id: %# at index %li", createLearnfestItem.itemId, (long)row);
[self.learnfestItemObjects insertObject:createLearnfestItem atIndex:row];
On didSelectRowAtIndexPath I want to receive the LearnfestItem from the NSMutableArray I do this by calling:
self.selectedLearnfestItem = [self.learnfestItemObjects objectAtIndex:indexPath.row];
Then I want to send it to another view controller to present the data I do this in the prepareForSegue segement:
LearnfestItemViewController * learnfestVC = [segue destinationViewController];
NSLog(#"Sending learnfest item with id: %#", self.selectedLearnfestItem.itemId);
learnfestVC.item = self.selectedLearnfestItem;
When I try to access my LearnfestItem's properties within cellForRowAtIndexPath. All I get is null... and so forth in my other table view delegate methods.. Can anyone spot what i'm doing wrong? Thanks
Code you show is missing all error and validity checking. When using NSAssert() and item.length/item.count checks, you will know what's going on.
However, based on your code I'd suggest two things:
All objects which also have mutable version should use "copy" properties. Now you have "strong" pointer to data received via init method, but if you reset the originally given data variable to point elsewhere (e.g. reusing one data variable inside for loop to init multiple items), then... I don't know what your LearnfestItem item properties will point to.
Make sure your LearnfestItemViewController *item is a copy, too.
My guess is that everything is initialised correctly, but the data disappears afterwards.
For example:
#property (nonatomic, copy) NSString * itemId; // Safe
#property (nonatomic, strong) NSString * itemId; // Not safe
When your property class has mutable variations (e.g. NSString vs. NSMutableString vs. MyMutableString), copy is safer. Using strong will create a pointer to original data, which could have been a mutable instance and could be modified afterwards. Using strong will always point to original data, even after it has been changed.
Second part:
learnfestVC.item = self.selectedLearnfestItem;
Your LearnfestItemViewController contains some property related to LearnfestItem class. Make sure it's a copy, too. When using segues, the calling object quite often just disappears. Make sure your new controller has local copy of all needed data (or use a protocol delegate, but that's a longer discussion)
Adding error and data validity checking will make your task a lot easier. Instead of trying to figure out afterwards why something doesn't work, you'll get notifications when something isn't as you're expecting.
Here's quick and dirty "maintenance" for your code. What you should get out of this is some ideas what to check, where and how. In normal situations this is overkill, but now you have a mysterious problem and need to find it. It can be hard and monotonous work.
#import Foundation;
#import UIKit;
#interface LearnfestItem : NSObject
#property (copy, nonatomic) NSString *itemId;
#property (copy, nonatomic) NSString *itemTitle;
#property (copy, nonatomic) NSString *itemDescription;
#property (copy, nonatomic) NSString *itemContent;
#property (copy, nonatomic) NSString *itemType;
#property (strong, nonatomic) UIImage *itemImage;
#property (strong, nonatomic) NSDate *itemRegistered;
- (instancetype)initWithData:(NSDictionary *)data andImage:(UIImage *)image;
#end
Object.m :
#import "Defaults.h"
#import "LearnfestItem.h"
#implementation LearnfestItem
- (instancetype)init
{
self = [self initWithData:nil andImage:nil];
return self;
}
- (instancetype)initWithData:(NSDictionary *)data andImage:(UIImage *)image
{
NSAssert(data.length, #"My Assert: missing data");
NSAssert(image, #"My Assert: missing image");
if ((self = [super init]))
{
// TODO: nil ok, if doesn't exist?
_itemId = data[ITEM_ID];
_itemTitle = data[ITEM_TITLE];
_itemDescription = data[ITEM_DESCRIPTION];
_itemContent = data[ITEM_CONTENT];
_itemType = data[ITEM_TYPE];
_itemImage = image;
_itemRegistered = data[ITEM_REGISTERED];
}
return self;
}
#end
"(original text) In my UIViewController I have a UITableView that creates a NSMutableArray of LearnfestItems within the cellForRowAtIndexPath method:"
NSAssert(learnfestItemDictionary.count, #"My Assert: missing dict");
NSAssert(learnfestItemImage, #"My Assert: missing image");
LearnfestItem *createLearnfestItem = [[LearnfestItem alloc] initWithData:learnfestItemDictionary andImage:learnfestItemImage];
NSLog(#"Insert learnfest item with id: %# at index %#", createLearnfestItem.itemId, #(row));
NSAssert(createLearnfestItem, #"My Assert: missing item");
NSAssert(self.learnfestItemObjects.count > row, #"My Assert: learnfestItemObjects");
self.learnfestItemObjects[row] = createLearnfestItem;
"(original text) On didSelectRowAtIndexPath I want to receive the LearnfestItem from the NSMutableArray I do this by calling:"
NSAssert(self.learnfestItemObjects.count > indexPath.row, #"My Assert: learnfestItemObjects");
self.selectedLearnfestItem = self.learnfestItemObjects[indexPath.row];
"(original text) Then I want to send it to another view controller to present the data I do this in the prepareForSegue segment:"
LearnfestItemViewController *learnfestVC = (LearnfestItemViewController *)[segue destinationViewController];
NSLog(#"Sending learnfest item with id: %#", self.selectedLearnfestItem.itemId);
learnfestVC.item = self.selectedLearnfestItem;

iOS: EXC_BAD_ACCESS (code=2, address=0x42)

I have a class which should hold some data:
the header:
#import <Foundation/Foundation.h>
#class Behandler, Tag;
#interface OeffnungsZeit2 : NSObject
#property (nonatomic, retain) NSNumber * offen_stunde;
#property (nonatomic, retain) NSNumber * offen_minute;
#property (nonatomic, retain) NSNumber * geschlossen_stunde;
#property (nonatomic, retain) NSNumber * geschlossen_minute;
#property (nonatomic, retain) Tag *tag;
#property (nonatomic, retain) Behandler *behandler;
-(void)setTag:(Tag *)tag;
-(Tag *)getTag;
-(void)setBehandler:(Behandler *)behandler;
-(Behandler *)getBehandler;
#end
the main file:
#import "OeffnungsZeit2.h"
#import "Behandler.h"
#import "Tag.h"
#implementation OeffnungsZeit2
#dynamic offen_stunde;
#dynamic offen_minute;
#dynamic geschlossen_stunde;
#dynamic geschlossen_minute;
-(void)setTag:(Tag *)tag{
self.tag = tag;
}
-(Tag*)getTag{
return self.tag;
}
-(void)setBehandler:(Behandler *)behandler{
self.behandler = behandler;
}
-(Behandler*)getBehandler{
return self.behandler;
}
#end
When I try to access it, somewhere in these lines:
NSArray *tage = [self fetchTageWithNummer:openDay];
Tag *tag = [tage objectAtIndex:0];
// create an OeffnungsZeit2 object and add it to our array
OeffnungsZeit2 *oeffnungsZeit2 = [[OeffnungsZeit2 alloc] init];
[oeffnungsZeit2 setTag:tag];
oeffnungsZeit2.offen_stunde = [NSNumber numberWithInt:[openHours intValue]];
oeffnungsZeit2.offen_minute = [NSNumber numberWithInt:[openMins intValue]];
oeffnungsZeit2.geschlossen_stunde = [NSNumber numberWithInt:[closeHours intValue]];
oeffnungsZeit2.geschlossen_minute = [NSNumber numberWithInt:[closeMins intValue]];
[self.oeffnungsZeiten addObject:oeffnungsZeit2];
I am getting an ECX_BAD_ACCESS error.
Can anyone hint me, why this is wrong? I am pretty new to iOS, and never wrote a class which holds data...
It looks like you are getting a stack overflow error when your getters/setters call themselves. For example, this code
-(void)setTag:(Tag *)tag{
self.tag = tag;
}
is infinite recursion, because self.tag = tag is another syntax for writing [self setTag:tag].
If you are using the latest Xcode, all you need to do is dropping the declarations and definitions of the getters and setters for tag and behandler that you have added manually. The compiler will generate the code that is equivalent, but has no infinite recursion.
Note: there are legitimate cases when you need to access the variable that "backs" an automatically generated property. By convention, the name of that variable is the name of the property prefixed with an underscore. You can override this in the #synthesize directive for the property.

How to save UITextField Text to NSString?

In my main view controller, I have a UITextField, and I am trying to save the text input into it to a NSString in my Homework model(class).
Homework.h
#import <Foundation/Foundation.h>
#interface Homework : NSObject
#property (nonatomic, strong) NSString *className;
#property (nonatomic, strong) NSString *assignmentTitle;
#end
Homework.m
#import "Homework.h"
#implementation Homework
#synthesize className = _className;
#synthesize assignmentTitle = _assignmentTitle;
#end
In my assignmentViewController, I create an object of type Homework and try to set it equal to whatever is entered into the the UITextField when the Save Button is pressed.
Assignment View Controller
#import <UIKit/UIKit.h>
#import "Homework.h"
#interface Assignment : UIViewController {
}
#property(nonatomic) IBOutlet UITextField *ClassNameField;
#property(nonatomic) IBOutlet UILabel *ClassNameLabel;
#property (weak, nonatomic) IBOutlet UIButton *SaveButton;
#property (nonatomic, strong) Homework *homeworkAssignment;
- (IBAction)Save:(UIButton *)sender;
#end
AssignmentViewController.m
- (IBAction)Save:(UIButton *)sender {
self.homeworkAssignment.className = self.ClassNameField.text;
NSLog(#"SaveButtonPressed %#", self.homeworkAssignment.className);
}
The NSLog prints out that className is (null). Can anyone help me figure out what I am doing wrong? This is my first ever iOS app (besides Hello World).
Edit: This is using ARC
Edit: I tried changing
self.homeworkAssignment.className = self.ClassNameField.text; to
self.homeworkAssignment.className = #"TEST";
and the log still shows (Null).
Double check you properly linked ClassNameField outlet and that you're initializing homeworkAssignment. Something like.-
self.homeworkAssignment = [[Homework alloc] init];
By the way, you should consider using camelCase notation for your variable names :)
Well to be honest the first steps are always hard but you should learn it the right way, héhé
First of all synthesize this way:
#synthesize labelAssignmentTitle,labelClassName;
or
#synthesize labelAssignmentTitle;
#synthesize labelClassName;
there is no need to do the following:
#synthesize className = _className;
#synthesize assignmentTitle = _assignmentTitle;
Now if you initialize the right way from the the start you'll find it a lot easier later!
HomeWork.h
#interface HomeWork : NSObject
#property (nonatomic, strong) NSString *className;
#property (nonatomic, strong) NSString *assignmentTitle;
-(id)initWithClassName:(NSString *)newClassName andAssignmentTitle:(NSString*)newAssigmentTitle;
HomeWork.m
#implementation HomeWork
#synthesize assignmentTitle,className;
-(id)initWithClassName:(NSString *)newClassName andAssignmentTitle:(NSString*)newAssigmentTitle {
self = [super init];
if(self){
assignmentTitle = newAssigmentTitle;
className = newClass;
}
return self;
}
#end
ViewController.m
- (IBAction)saveIt:(id)sender {
HomeWork *newHomeWork = [[HomeWork alloc]initWithClassName:[labelClassName text]andAssignmentTitle:[labelAssignmentTitle text]];
}
Because of this, you directly make a newHomeWork object with the parameters given by your two UITextFields.
Now print it out in your logmessage and see what happends ;)

Issue passing object to NSMutableArray in AppDelegate

I'm having trouble making a shopping cart sort-of concept in my app. I have my AppDelegate (named ST2AppDelegate) that contains an NSMutableArray called myCart. I want RecipeViewController.m to pass an NSString object to myCart, but every time I pass it the NSString and use NSLog to reveal the contents of the array, it is always empty.
Can anyone please tell me what I am doing wrong? I have worked on this code for days, and there is a line of code in which I don't understand at all what's going on (in the RecipeViewController.m, labeled as such).
Any help would be so appreciated... I'm just a beginner. Here are the relevant classes:
ST2AppDelegate.h:
#import <UIKit/UIKit.h>
#interface ST2AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) NSMutableArray* myCart;
- (void)addToCart:(NSString*)item;
- (void)readCartContents;
#end
ST2AppDelegate.m:
#import "ST2AppDelegate.h"
#implementation ST2AppDelegate
#synthesize myCart;
// all the 'applicationDid...' methods...
- (void)addToCart:(NSString *)item
{
[self.myCart addObject:item];
}
- (void)readCartContents
{
NSLog(#"Contents of cart: ");
int count = [myCart count];
for (int i = 0; i < count; i++)
{
NSLog(#"%#", myCart[count]);
}
}
#end
RecipeDetailViewController.h:
#import <UIKit/UIKit.h>
#import "ST2AppDelegate.h"
#interface RecipeDetailViewController : UIViewController
#property (nonatomic, strong) IBOutlet UILabel* recipeLabel;
#property (nonatomic, strong) NSString* recipeName;
#property (nonatomic, strong) IBOutlet UIButton* orderNowButton;
- (IBAction)orderNowButtonPress:(id)sender;
#end
RecipeDetailViewController.m:
#import "RecipeDetailViewController.h"
#implementation RecipeDetailViewController
#synthesize recipeName;
#synthesize orderNowButton;
// irrelevant methods...
- (IBAction)orderNowButtonPress:(id)sender
{
// alter selected state
[orderNowButton setSelected:YES];
NSString* addedToCartString = [NSString stringWithFormat:#"%# added to cart!",recipeName];
[orderNowButton setTitle:addedToCartString forState:UIControlStateSelected];
// show an alert
NSString* addedToCartAlertMessage = [NSString stringWithFormat:#"%# has been added to your cart.", recipeName];
UIAlertView* addedToCartAlert = [[UIAlertView alloc] initWithTitle:#"Cart Updated" message:addedToCartAlertMessage delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[addedToCartAlert show];
// add to cart (I don't understand this, but it works)
[((ST2AppDelegate*)[UIApplication sharedApplication].delegate) addToCart:recipeName];
// read cart contents
[((ST2AppDelegate*)[UIApplication sharedApplication].delegate) readCartContents];
}
#end
You need to initialize myCart when your application launches:
self.myCart = [[NSMutableArray alloc] init];
otherwise you are just attempting to add objects to a nil object which while it won't throw an exception because of the way objective-c handles nil objects it will not function as expected until you initialize it.
Do you ever initalize the shopping cart variable?
Try doing lazy instantiation.
-(NSMutableArray *) myCart{
if (!_myCart){
_myCart = [[NSMutableArray alloc] init];
}
return _myCart;
}
This way you will know it will always get allocated. Basically, this method makes it so that whenever someone calls your classes version of the object it checks to see if that object has been allocated and then allocates it if it has not. It's a common paradigm that you should employ with most of your objects.
This method should go in the app delegate (where the object was declared).

iOS Won't pass variable to child view?

I have faced a problem I can not see the problem to. I am trying to pass a simple NSString to a child variable but it continues to return as null even when I use NSLog to show there is a string in the variable.
The variable finalDate will not pass to the child view.
Parent View
ChangeTimeViewController *ChangeTimeView = [[ChangeTimeViewController alloc] init];
NSLog(#"%#", date);
ChangeTimeView.finalDate = date;
[ChangeTimeView setDelegate:self];
[self.navigationController pushViewController:ChangeTimeView animated:YES];
Child View .H
#import <UIKit/UIKit.h>
#protocol ChangeTimeViewControllerDelegate;
#interface ChangeTimeViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource>
#property (nonatomic, weak) id <ChangeTimeViewControllerDelegate> delegate;
#property (nonatomic) NSString *enteredTime;
#property (nonatomic, strong) UIPickerView *UIPicker;
#property (nonatomic, strong) NSString *finalDate;
#end
#protocol ChangeTimeViewControllerDelegate <NSObject>
- (void)childTimeViewController:(ChangeTimeViewController *)viewController didChooseValue:(NSString *)string;
#end
Child View .M
NSLog(#"%#", self->finalDate);
What you are doing is perfectly fine. You should insert the NSLog in the view(Did/Will)Appear or some similar method and you may use the self.finalDate notation to make sure you don't try to read some uninitialized ivar.
Note: properties synthesize ivars with _ as prefix (_finalDate is the correct storage unless you synthesized it it with some other name)
If you want to make sure that all input parameters are passed to the view controller, then create an init method for it. Similar to this:
- (id)initWithDate:(NSDate*)date delegate:(id)delegate
Pass NSString As ChangeTimeView.finalDate = #"This Is my Simple String"; and use/put NSLog in viewDidLoad method for show is it rich at nextViewController or not ?? Otherwise if your date (NSString) is proper then Your code is correct.
Check what is happening if you set like,
ChangeTimeView.finalDate = #"MyString";
and in view.m log NSLog(#"%#", self.finalDate);

Resources