Objective-C: Property not found on object of type - objective-c

So I am very new to Objective-C and I was trying to expand on a working program I made from a lesson book.
So, I have a working class names "Item". Here is Item.h:
#interface Item : NSObject
{
NSString *_itemName;
NSString *_serialNumber;
int _valueInDollars;
NSDate *_dateCreated;
}
+(instancetype)randomItem;
It has a few functions to manipulate those variables, initialize them in a costume way and print them in a formatted manner.
Then I made a sub-class of "Item" called "Container". I wanted it to be similar to "Item" but have a NSMUtableArray variable that could hold an Array of "Item"s. Here is Container.h:
#import "Item.h"
#interface Container : Item
{
NSString *_containerName;
NSDate *_containerDatecreated;
int _containerValueinDollars;
NSMutableArray *_subItems;
}
+(instancetype) randomContainer;
Now in my main. I do a number of things with the "Item" class and they work fine. But when I do this:
Container *container=[Container randomContainer];
for (NSString *item in container.containerName) {
NSLog(#"%#", item);
}
It says "Property 'containerName' not found on object of type 'Container *'". But I clearly added that variable to the "Container" subclass.
Note: randomContainer creates a random container instance and does some initializing to it. When I debug in Xcode it seems to be working fine.
What is my problem?

container.containerName is called dot syntax. It is the equivalent of [container containerName]. But you don't have a property or method on your class named containerName so the compiler is complaining.
Now, that is likely entirely baffling in the context of the book you are using. And that is because the book you are using is way way way out of date.
If it were up to date, your Item class would look more like:
#interface Item:NSObject
#property(copy) NSString *containerName;
#property(copy) NSDate *containerDateCreated;
#property(assign) NSInteger containerValueInDollars;
... etc ...
#end
Get yourself an up to date set of tutorials and/or books before learning anything else. It'll save a lot of frustration.

Related

How to reference objects in NSMutableSets

I have a small C# library that I am trying to port (actually re-writing the C# code using it as a guide) to Obj-C. The purpose of the library is to generate recurring appointments for an iOS calendar app. I'm having problems porting C# structs to Obj-C objects. This is what I have for one of the structs that holds the appointment info:
#interface Appointment : NSObject {
#public
NSDate *apptStartTime;
NSDate *apptEndTime;
NSString *key;
}
#end
One of the methods I wrote accepts a date, a set of schedules (also a port of a C# struct) and the appointment's list (I'm using a NSMutableSet which contains the Appointment interface above). If I can get the appointments method working, I'm pretty sure I can figure out the remainder (I think). This is the code that adds appointments to the NSMutableSet:
-(void) addAppointmentsForDate:(NSDate *)checkDate andSchedules: (NSMutableSet *)schedules andAppt:(NSMutableSet *)appointments {
Appointment *appt = [[Appointment alloc]init];
for(NSMutableSet *schedule in schedules) {
if(schedule.occursOnDate(checkDate)) {
appt = [self generateAppointment:checkDate andSchedule: [schedules removeObject:schedules]];
[appointments addObject: appt];
}
}
}
-(Appointment *) generateAppointment: (NSDate *) checkDate andSchedule: (Schedule *) schedule {
Appointment *appt = [[Appointment alloc]init];
appt->apptStartTime = schedule->timeOfAppointment;
appt->apptEndTime = nil; // TODO
appt->key = schedule->key;
return appt;
}
I'm getting build errors on the if statement:
Sending 'void' to parameter of incompatible type 'Schedule *'
I have never used NSMutableSets before, nor have I tried to port from C# before. I'm having a time with the port of the C# struct's, as you can see. I have read all of the Apple docs on sets, and several docs that explain the differences between C# and Obj-C.
Can somebody please either explain what I'm doing wrong, or point me to some good docs that can give me an example of referencing elements within sets?
Instead of this:
#interface Appointment : NSObject {
#public
NSDate *apptStartTime;
NSDate *apptEndTime;
NSString *key;
}
#end
please please please write
#interface Appointment : NSObject
#property (readwrite, nonatomic, strong) NSDate* startTime;
#property (readwrite, nonatomic, strong) NSDate* endTime;
#property (readwrite, nonatomic, strong) NSString* key;
#end
Don't make instance variables public. Instance variables should never, ever be accessed directly outside code belonging to that class.
Always start instance variables with an underscore character, like _startTime. That way any access to an instance variable stands out. (The code above will create instance variables for you).
Use accessors unless you have a very, very good reason not to.
Looking at the code, I have the impression that you are not quite sure whether you are writing C++ or Objective-C code.
if(schedule.occursOnDate(checkDate))
How do you call an Objective-C method again?
for(NSMutableSet *schedule in schedules)
Are you sure about this? schedules is an NSMutableSet*. So you are saying that the elements of your schedules mutable set are again mutable sets? That is quite an unusual thing to have, but if you say so...
appt = [self generateAppointment:checkDate andSchedule: [schedules removeObject:schedules]];
This is more than weird. What do you think is
[schedules removeObject:schedules]
going to do? You expect a set to have itself as an element?
My advice: Go to bed. Have ten hours of sleep. After a good breakfast, have a look at your code again.

How to use #property correctly (Setters) within another class

another question i'm trying to use a setter within another class but I seem to get this odd error here is the code below:
AppDataSorting.h
#import <Foundation/Foundation.h>
#interface AppDataSorting : NSObject{
NSString *createNewFood;
NSNumber *createNewFoodCarbCount;
}
#property (readwrite) NSString *createNewFood;
#end
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
}
- (IBAction)saveData:(id)sender {
NSLog(#"%#", self.foodName.stringValue);
self.createNewFood = self.foodName.stringValue;
NSLog(#"%.1f", self.carbAmount.floatValue);
}
#end
I get the error message in AppDelegate.m which is: Property 'createNewFood' not found on object of type 'AppDelegate *'
Could someone please explain the issue here?
You declare this property:
#property (readwrite) NSString *createNewFood;
In AppDataSorting.h so you can access it like self.createNewFood in AppDataSorting.m file not AppDelegate.m. If you want to call it as you do in AppDelegate.m you have move this line:
#property (readwrite) NSString *createNewFood;
to AppDelegate.h file.
Or if you want to use property from AppDataSorting class in AppDelegate you have to create object and call it on that object:
- (IBAction)saveData:(id)sender {
NSLog(#"%#", self.foodName.stringValue);
AppDataSorting *dSorting = [[AppDataSorting alloc] init];
dSorting.createNewFood = self.foodName.stringValue;
NSLog(#"%.1f", self.carbAmount.floatValue);
}
In -saveData:, self refers to an instance of NSAppDelegate. The createNewFood property is defined on instances of the class AppDataSorting.
Also note that Cocoa/CF naming conventions give special meaning to methods that start with "init", "new" and (to a lesser degree) "create". You probably want to avoid such things in your property names. Details here.
In general, properties should represent conceptual "properties" of an object. So if you had a Person class, it might have a name property, but it wouldn't have a createNewOutfit property.
You need to access createNewFood on an instance of AppDataSorting - but you're trying to access the property on the AppDelegate-class which clearly doesn't implement it.
So you would need to create an instance of AppDataSorting and then access the property like so:
AppDataSorting *instance = [[AppDataSorting alloc] init];
instance.createNewFood = self.foodName.stringValue;
Final notes:
The docs provide a good base of information
If you don't need atomicity you should always declare properties with the nonatomic attribute
createNewFood is not a good name for a property since it suggests a method which creates new food - yet it's only meant to store data (in this case an NSString instance)

How do I get the Objective-C class of an ivar?

I have a bunch of simple NSManagedObjects I create in a unit test. They just have a single name attribute of type NSString *. I always give my NSManagedObject the same entityName and Class name.
I want to avoid having to write the following code 30 times to set up a unit test:
#interface FooTest : GHTestCase {
Foo *foo;
}
#end
#implementation FooTest
- (void) setUp {
[super setUp];
foo = [NSEntityDescription insertNewObjectForEntityForName:#"Foo"
inManagedObjectContext:managedObjectContext];
foo.name = #"foo";
}
#end
Since foo is an ivar, I would think I should be able to write a macro to grab the type of foo (Foo), and use to create my Foo:
#define InsertManagedObjectByVariable(variable) \
do { \
variable = [NSEntityDescription insertNewObjectForEntityName:NSStringFromClass([typeof(variable) class])]; \
variable.name = (NSString *) CFSTR(#variable);
} while(0)
However, this causes the following warning in clang:
variable = [NSEntityDescription insertNewObjectForEntityName:NSStringFromClass([typeof(variable) class])];
^
Expected expression
I also thought I could try to determine the type using the objective-c runtime IVar from Ivar class_getInstanceVariable(Class cls, const char* name), but the only IVar type information available from the type encoding from ivar_getTypeEncoding is id, which isn't enough.
Can someone think of a way to obtain the type information of an IVar either at compile time or runtime?
I haven't tried obtaining class information from an ivar, but I know that #property declarations do encode information about the class. For instance, this property declaration:
#property (copy) NSString *normalString;
results in this attribute string (retrieved using property_getAttributes()) at runtime:
T#"NSString",C,VnormalString
I've written some open source parsing code for this information.
Once you have the class name, you can convert it into an actual Class object using NSClassFromString(), and message the result from there.
Disclaimer: This probably shouldn't be depended upon for production applications, as it is undocumented.
An id is an id. At runtime, all Objective-C objects have the same type (objc_object). This is tied up in the dynamic nature of ObjC. For example, an object can change classes at runtime, new classes can be created, and the class hierarchy can change. You can ask a specific instance what its type is (since this is stored in objc_object), but a pointer to an object is just a pointer to an object. Even less than that: it's really just a pointer to a C struct that happens to have extra memory allocated at the end (to hold subclass ivars).
Your macro seems interesting, but you'll probably need to pass the classname as the second parameter rather than autodetecting it.
Maybe i misunderstand what you are trying to achieve.
To get the class of an iVar, can't you use the class method of the iVar?
like:
NSString *aString = #"random string";
NSLog(#"%#",NSStringFromClass([aString class]));

Why am I getting "incompatible pointer type"?

I am trying to create a custom object that simply inherits the NSString class and overrides the 'description' method.
When I compile, however, I am getting a warning:
Incompatible pointer types initializing 'OverrideTester *' with an expression of type 'NSString *'
Here is my code:
main.m
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>
#import <Foundation/NSAutoreleasePool.h>
#import "OverrideTester.h"
int main (int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *str = #"Programming is fun";
OverrideTester *strOverride = #"Overriding is fun";
NSLog (#"%#", str);
NSLog (#"%#", strOverride);
[pool drain];
return 0;
}
OverrideTester.h
#import <Foundation/Foundation.h>
#interface OverrideTester : NSString
-(void) description;
#end
OverrideTester.m
#import "OverrideTester.h"
#implementation OverrideTester
-(void) description
{
NSLog(#"DESCRIPTION!\n");
}
#end
NSString is part of a class cluster. You cannot just create arbitrary subclasses of it, and when you do, you can't assign constant strings to them (which are type NXConstantString). See Subclassing Notes in the NSString documentation. Generally you don't want to subclass NSString. There are better solutions for most problems.
you are assigning an instance of NSString to your variable of type OverrideTester. If you want an instance of your class, you need to instantiate an instance of that class; type-casting will never change the class of an instance.
description is defined as returning an NSString*:
- (NSString *)description;
Do not try to learn about subclassing and overriding methods by subclassing NSString (or any other class cluster). If you want to play with subclassing and such -- a very good idea when new to the language, assuredly -- then subclass NSObject, potentially multiple levels , and play there.
How do you mean to subclass NSObject,
potentially multiple levels? Isn't it
possible NSObject might have
conflicting methods compared to other
class clusters or just not have them
available to override?
If your goal is to figure out how method overrides work (which I thought it was), then you'd be better off doing it entirely yourself.
I may have mis-read your question.
In any case, subclassing NSString is pretty much never done. There are very very few cases where it is useful. Overriding description in anything but custom classes specifically for debugging purposes is useful, yes. Calling description in production code should never be done.
Also, why would description return an
NSString* in this code?
What would happen if something that expects an NSString* return value were to call your version that doesn't return anything?
A crash.
You are declaring a variable named strOverride of type pointer to OverrideTester. But to that variable, you are trying to assign a pointer to an NSString. You cannot assign a superclass to a variable of a subclass. Imagine a generic class TwoWheeled and a derived class Motorbike. A Motorbike can be treated like a TwoWheeled, but not the other way round as the Motorbike has features a normal TwoWheeled might not have like a motor.

Objective-C : adding attribute to a category

I have built a category for NSDate and I would like to encapsulate an attribute in this category to hold some data. But I can't achieve adding this attribute, only methods.
Is there any way to achieve this ?
Thank you.
Here some Code:
Filename: NSObject+dictionary.h
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface NSObject (dictionary)
- (NSMutableDictionary*) getDictionary;
#end
Filename: NSObject+dictionary.m
#import "NSObject+dictionary.h"
#implementation NSObject (dictionary)
- (NSMutableDictionary*) getDictionary
{
if (objc_getAssociatedObject(self, #"dictionary")==nil)
{
objc_setAssociatedObject(self,#"dictionary",[[NSMutableDictionary alloc] init],OBJC_ASSOCIATION_RETAIN);
}
return (NSMutableDictionary *)objc_getAssociatedObject(self, #"dictionary");
}
Now every instance (of every class) has a dictionary, where you can store your custom attributes.
With Key-Value Coding you can set a value like this:
[myObject setValue:attributeValue forKeyPath:#"dictionary.attributeName"]
And you can get the value like this:
[myObject valueForKeyPath:#"dictionary.attributeName"]
That even works great with the Interface Builder and User Defined Runtime Attributes.
Key Path Type Value
dictionary.attributeName String(or other Type) attributeValue
You can't add instance variables in categories.
However, you can add storage for your attribute to an object using associative references. Note that if you need to add more than one attribute, rather than adding an associative reference for each, you're probably better off adding a single reference to (say) an NSMutableDictionary, CFMutableDictionaryRef, or NSMapTable and using that for all of your attributes.
objc_setAssociatedObject() and objc_getAssociatedObject()
If you want to add attribute to class, you can try to use github.com/libObjCAttr. It's really easy to use, add it via pods, and then you can add attribute like that:
RF_ATTRIBUTE(YourAttributeClass, property1 = value1)
#interface NSDate (AttributedCategory)
#end
And in the code:
YourAttributeClass *attribute = [NSDate RF_attributeForClassWithAttributeType:[YourAttributeClass class]];
// Do whatever you want with attribute
NSLog(#"%#", attribute.property1)