How to persist objects between classes - objective-c

I have 6 categories that contain unique data; I have defined a class that looks like this:
#interface ExportBookData : NSObject {
}
#property (strong, nonatomic) NSArray *booksArray;
#property (nonatomic, retain) NSMutableDictionary *builtFileList;
#property (nonatomic, retain) NSMutableArray *exportData;
#end
What I want to do is be able to instantiate the class ExportBookData, once for each category, and use that instantiated class throughout another class, having the data persist and be accessible between classes.
I have tried this code, but it doesn't do what I need:
ExportBookData *abe = [ExportBookData new];
abe.abeBuiltFileList = [NSMutableDictionary dictionary];
abe.abeExportData = [NSMutableArray arrayWithCapacity:abe.abeBooksArray.count];
UPDATE The problem is in the addressing of the objects; I have categories named Abe, Balls, Comp, Caller, Hut, and House. I want the class to have properties that can be addressed as Abe, Balls, etc. I can't figure out how to do that with what I have defined.
I have looked through Google, but found nothing that answers my specific question.

Encapsulate, encapsulate, encapsulate! Put the special knowledge inside the class itself.
Let's say you have an ExportBookData object that behaves differently depending which bookseller it uses. Then provide an initializer that takes a bookseller type:
ExportBookData *abe = [[ExportBookData alloc] initWithCategory:#"Abe"];
Okay, so now this instance of ExportBookData knows that its behavior should be Abe-type behavior. But no matter how an ExportBookData is initialized, its public property names will all be the same, e.g. builtFileList and exportData, so you'll then be able to refer to abe.builtFileList and this will be the right kind of list for an Abe.

Related

Conflict between declaring instance variable and property

I am studying Objective-C. I asked a question about this code earlier but I came up with further questions. The below code is trying to make NSArray externally but really makes NSMutableArray internally so I can add pointers or remove in NSMutableArray
I face two questions.
1) What is the purpose of doing like this? Is there a specific reason you make NSArray externally? Why can't I just declare a property of NSMutableArray?
2)I learn that instance variable (_assets) is made when I declare a property of NSArray *assets. And I also declared NSMutableArray *_assets under the interface. I think those two _assets conflict each other even though they have different types. Am I thinking this in a wrong way?
#interface BNREmployee : BNRPerson
{
NSMutableArray *_assets;
}
#property (nonatomic) unsigned int employeeID;
#property (nonatomic) unsigned int officeAlarmCode;
#property (nonatomic) NSDate *hireDate;
#property (nonatomic, copy) NSArray *assets;
I'll try put your answers the way you have asked them. Let hope they clear your doubts. By now I guess you would be knowing that NSArray once initialised with data you wont be able to add or delete the data inside it which is different from NSMutableArray.
The benefit here no one else can change your externally visible data. Also when you try to sort or iterate the array you are sure that no other data would be removed or added. Also if you use NSMutableArray for such cases the application would crash if you add data while you iterate the array.
Like #KirkSpaziani Explained
#synthesize assets = _assets;
would create an instance variable for your property. However you are actually supposed to use this _assets only in getter and setter. Else places you should be using self.assets.
You can also synthesize your other array NSMutableArray *_assets as follows
#synthesize _assets = __assets;
Which would have double underscore, but frankly we shouldn't be using the underscore for a starting variable name. Plus would be great if you have different names altogether.
Also with advances in Objective C you dont require to synthesize these variables at all. Just use the self.variableName and you can access it.
Hope it clears some of your queries.
Put
{
NSMutableArray *_assets;
}
in the #implementation block
#implementation {
NSMutableArray *_assets;
}
Putting the NSMutableArray in the implementation block hides the fact that it is mutable from consumers (it is no longer in the header file).
Follow it with:
#synthesize assets = _assets;
This might not be necessary actually, but makes things clearer. When you declare a property an ivar will be automatically created (unless you #dynamic the property). However an explicitly declared ivar of the same name will override the automatically created one - so long as the type is the same or a subclass.
The reason to make it an NSArray publicly visible is so that no one else can mutate your data structure. You will have control of it. If it is an NSMutableArray internally then you can add and remove items without exposing that functionality to consumers.
You can declare your property to be readonly or readwrite - a readwrite NSArray means you can replace the whole array with a property set, but you can't add or remove items. If internally you are adding and removing items, this can make things messy. Try to stick with readonly when having a mutable internal version.
Here's something you can do if you want _assets to be a mutable array, but you don't want other classes to modify it, implement the setter and getter of the assets property so they look like this (implementing the getter and the setter will cause the property to not be synthesised, which means the NSArray *_assets will not be created automatically):
-(NSArray *)assets{
return [_assets copy]; // Copy creates an immutable copy
}
-(void)setAssets:(NSArray *)assets{
_assets = [NSMutableArray arrayWithArray:assets];
}
Keep in mind that if you access the assets array a LOT, it might be slow since you're creating an immutable copy every time, so you can create an NSArray whenever your _assets array is modified and return that in the -(NSArray *)assets method
The reason you'd internally keep an NSMutableArray, but expose an NSArray externally is so that users of your API won't abuse it and mutate its data. Keeping it visible as immutable makes people less prone to mess with it.
Another approach you could take to this is to not use a property at all, but simply have a getter and a mutable property in a class extension. For example, in your .h:
#interface BNREmployee : BNRPerson
- (NSArray *)assets;
#end
In your .m
#interface BNREmployee ()
// Inside of the class manipulate this property
#property (nonatomic, strong) NSMutableArray *mutableAssets;
#end
#implementation BNREmployee
// Clients of your class rely on this
- (NSArray *)assets
{
// copy makes the result immutable
return [self.mutableAssets copy];
}
#end
Another approach might be to make the property only writable to the implementation of you class.
To do that you declare your property as readonly in the header:
//BNREmployee.h
#property (nonatomic, readonly) NSMutableArray *assets;
Than declare it as readwrite inside an inner interface in your implementation:
//BNREmployee.m
#interface BNREmployee()
#property (nonatomic, readwrite) NSMutableArray *assets;
#end
#implementation
...

Custom class NSObject not key value coding compliant [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why is my object not key value coding-compliant?
I'm having a dictionary and I want to add keys/values to a custom class, but i always get the error, that the class is not KVC compliant, but the Apple documents state that it should be.
My code:
ContactObject.h:
#interface ContactObject : NSObject
+ (ContactObject *)testAdding;
#end
ContactObject.m:
#implementation ContactObject
- (id)init {
self = [super init];
if (self) {
// customize
}
return self;
}
+ (ContactObject *)testAdding
{
// create object
ContactObject *theReturnObject = [[ContactObject alloc] init];
[theReturnObject setValue:#"Berlin" forKey:#"city"];
[theReturnObject setValue:#"Germany" forKey:#"state"];
return theReturnObject;
}
#end
I think I'm missing something very stupid :)
Please, any help appreciated ...
Greetings,
matthias
Actually to be KVC compliant:
How you make a property KVC compliant depends on whether that property is an attribute, a to-one relationship, or a to-many relationship. For attributes and to-one relationships, a class must implement at least one of the following in the given order of preference (key refers to the property key):
The class has a declared property with the name key.
It implements accessor methods named key and, if the property is mutable, setKey:. (If the property is a Boolean attribute, the getter accessor method has the form isKey.)
It declares an instance variable of the form key or _key.
I don't see any of these three implemented. You need to have at least properties that you are trying to set through KVC, the default NSObject implementation is able to set properties through setValue:forKey: but you must declare them.
You need to declare every property that will be used:
#interface ContactObject : NSObject
#property (nonatomic,copy, readwrite) NSString* city;
#property (nonatomic, copy, readwrite) NSString* state;
+ (ContactObject *)testAdding;
#end
Or use a NSMutableDictionary object.
For example:
NSMutableDictionary* dict= [NSMutableDictionary new];
[dict setObject: #"Berlin" forKey: #"city"];
[dict setObject: #"Germany" forKey: #"state"];
You need to actually declare/implement properties. Key-Value Coding doesn't mean that every NSObject is automatically a key/value dictionary.
In this case you would need to declare:
#property (nonatomic, readwrite, copy) NSString* city;
#property (nonatomic, readwrite, copy) NSString* state;
in your #interface declaration.
ObjC is dynamic in some ways, but it's not really dynamic as far as storage in classes. If you want ContactObject to be KVC-compliant for certain keys, those keys need to exist in the class. The KVC Guide has this to say:
For properties that are an attribute or a to-one relationship, this
requires that your class:
Implement a method named -<key>, -is<Key>, or have an instance
variable <key> or _<key>. Although key names frequently begin with a
lowercase letter, KVC also supports key names that begin with an
uppercase letter, such as URL.
If the property is mutable, then it should also implement -set<Key>:.
Your implementation of the -set<Key>: method should not perform
validation.
The easiest way to accomplish that is to declare the keys you want as properties:
#property (copy, nonatomic) NSString * city;
#property (copy, nonatomic) NSString * state;
You can also declare an ivar and implement accessors yourself, but there's usually no good reason to do it that way -- declared properties will take good care of you.

Is there a way to have pseudo property in Objective-C?

Let say if there is already class properties of
#property (strong, nonatomic) JJNode *leftChild;
#property (strong, nonatomic) JJNode *rightChild;
and the app already makes extensive use of if (parent.leftChild) { ... } and parent.leftChild = newNode (both getter and setter).
But say, the class might work better if the left and right child can be represented by an NSMutableArray object, so that the class can support N-children in the future, and looping through the children is easier.
So it will be
#property (strong, nonatomic) NSMutableArray *childrenArray;
and in some cases, the children can be iterated by
for (JJNode *node in self.childrenArray) { ... }
But using this new array, can we still be able to keep on using parent.leftChild and parent.leftChild = newNode?
I wonder if it is a good practice, as it may seem like parent.leftChild and (JJNode *)[parent objectAtIndex: 0] are different objects, but in fact the same thing. But say if we go ahead and do it, can we have pseudo property to achieve that?
It seems that we can actually use #property (strong, nonatomic) JJNode *leftChild; and change the getter and setter to actually use the array, but there will be two extra instance variables. Can it be done without the ivars? Or can we define 2 methods so that parent.leftChild = newNode will actually invoke some setter method, and parent.leftChild will invoke a getter?
Your properties are not limited to synthesized ones - in fact, they are no more than a pair of methods that follow a certain naming convention.
You can remove the #synthesize instructions for the leftChild and rightChild, and replace them with methods that get/set the first and the second elements of the NSArray that holds nodes in case when there are more than two.

Memory semantics of a computed array property?

This is for an app that allows users to tag things. Tags are just strings.
An array of TagHolder objects holds a list of all tags in use in the app, with a boolean telling if the tag is selected, but this is an implementation detail.
The external interface calls for two methods, selectedTags, and setSelectedTags: which return and accept an arrays of strings.
I would like these two methods to work as accessors for a declared property selectedTags.
Now, my question is:
What would be the correct memory management semantics to declare for that property?
The code pattern that I have in mind is this (code not tested, so please bear with typos):
#interface TagInfo : NSObject
#property (strong, nonatomic) NSString *tag;
#property (nonatomic) BOOL selected;
#end
#interface SomeClass : NSObject
#property (memorytype, nonatomic) NSArray *selectedTags;
#end
#implementation TagHolder
- (NSArray *)selectedTags
{
// tagInfoArray is an array of all TagInfo objects
NSPredicate *selPred = [NSPredicate predicateWithFormat: #"selected == YES"];
NSArray *selectedTagInfoObjects = [[self tagInfoArray] filteredArrayUsingPredicate: selPred];
NSArray *selectedTags = [selectedTagInfoObjects valueForKey: #"tag"];
return selectedTags;
}
- (void)setSelectedTags: (NSArray *)selectedTags
{
for (TagInfo *tagInfo in [self tagInfoArray]) {
tagInfo.selected = [selectedTags containsObject: tagInfo.tag];
}
}
#end
What should memorytype be? Obviously not strong or weak, but I think it could be any one of assign, copy or even unsafe_unretained, but which one is the most correct for a computed property with an object value?
I normally use ARC, but I guess the question is the same in an environment with manual retain count.
memorytype is significant only when you #synthesize your property accessors. Since you are providing your own implementation for both the getter and the setter, the things you put in parentheses after #property are ignored; I usually put readonly or readwrite there, just to remind myself of what kind of access is available on these properties.
Your code is correct, it will work without creating memory issues with or without ARC.

How to synthesize members from another class

I have created a certain class to represent an object. My program has several views and view controllers. I want the different view controllers to work with this object. However, when I try to synthesize the class members so that they can be used in the view controller, I get an error saying that there is no declaration of the property found in the interface, even after I have included the .h file. How can I synthesize members from another class in the various view controllers that will be working with them?
Without seeing your code or the specific compiler errors, it's hard to say what might be going on.
If you're just setting up a simple model object, then you should be able to write something like this in Model.h:
#interface Model : NSObject {}
#property (nonatomic, retain) NSString *name;
#end
and in your Model.m file:
#implementation Model
#synthesize name;
#end
All you need to do at that point is #import "Model.h" into your other source files and then you can use the name property like so:
Model *m = [[Model alloc] init];
m.name = #"Bob";
...
It sounds like the compiler is complaining about the lack of the #property declaration in your case.