I run into design choices like this often and struggle a bit; I'm looking for some other perspectives.
I often want to keep lists of, or pass around chunks of state that are basically just sets of values. The values tend to be primitive types: floats, NSTimeIntervals, CGPoints, etc.
My first inclination is often to create C structures for these sets of properties, e.g.
typedef struct _STATE {
float foo;
NSTimeInterval elapsed;
CGPoint point;
} STATE;
etc.
But C structures don't play nicely with the native Cocoa collection classes (NSArray, NSSet, NSDictionary), and using overmany of them to track lots of state feels like it runs against the grain of rest of my Cocoa-friendly code-- I end up having and directly managing arrays of structs, and passing struct pointers around in messages, etc.
On the other hand, since raw performance isn't necessarily critical, I could encode these values into a NSDictionary, wrapping them all in NSValue or NSNumber, but the resulting syntax is hardly terse, and a little fragile, requiring type and name correctness at runtime for both the insert and the lookup:
[stateDict setObject:[NSNumber numberWithFloat:foo] forKey:#"bar"];
...
float something = [[stateDict objectForKey:#"bar"] floatValue];
and some types, like NSTimeInterval, are only able to be used with some (arguable) hackery (typecast to double in that case).
Finally, I could create data-only container objects, with private member data and only getters/setters. (These would be called "beans" in Java.) These are more terse to access than dictionaries, more Cocoa than structs, but feel like overkill to me, especially if I only need them as "inner classes" that are used for state management internal to a single object type.
How do you, great Cocoa programming public, do this?
Depending on the situation, I run either with using NSDictionary classes for arbitrary data, or I create container classes (the #property/synthesize tags in Objective C make this really easy). By using ObjC for the header file:
#interface StateObject : NSObject {
NSNumber *foo;
NSTimeInterval *elapsed;
CGPoint point;
}
#property (retain) NSNumber *foo;
#property (retain) NSTimeInterval *elapsed;
#property (copy) CGPoint point;
#end
One can then use #synthesize <variable> in the .m file to automatically create the setters/getters. Then, while anonymous NSNumbers are still ornery, you can do:
myStateObject.foo = [NSNumber numberWithFloat:7.0];
This should take most of the pain away, and let you use the Cocoa collection classes to better shuffle data around.
Not necessarily endorsing this approach as "best", but there is a middle ground between your proposals: create C structs to hold the information, and then wrap the structs in NSValue objects when you need to put them into Cocoa data structures. You can see UIKit do this in some cases with structs like CGPoint in notifications (and I'm sure that AppKit does, as well).
See "Using Values" in Number and Value Programming Topics for Cocoa for more on that.
Related
Coming from Java et al, I'm not clear on the difference between these two declarations:
#property (nonatomic, readwrite) NSInteger score;
#property (nonatomic, strong) NSMutableArray *cards;
Why is the pointer, *, not a requirement on both property declarations?
I've seen this a lot in local variables too:
- (void)viewDidLoad
{
[super viewDidLoad];
int foo = 1;
NSString *bar = #"foo";
}
What's the difference between static allocation of primitive type int and NS types?
Objective-C objects are always allocated on the heap, so you always access them through pointers. Variables of primitive (or struct) types can be, and typically are, allocated on the stack and accessed without pointers.
If you're familiar with Java, it's basically the same semantics. Primitive types are accessed and passed around by value, objects by reference. The difference is that ObjC has (by virtue of its C lineage) syntax explicitly marking that difference.
Type names that start with an uppercase prefix in Apple frameworks aren't all ObjC classes. NSInteger is a primitive type just like int, so you can and usually do use it without pointers.
pointer is always used for referring to something at the heap but not when you referring to something on the stack.But
for some primitive types and simple structure which are accessed via the stack so you don't need to use pointer..
NSInteger is a primitive type, that means it will be stored locally on the stack. there is no need to use a pointer to access it, but if you want to use pointer then you can.
You can have a pointer to an NSInteger if you really want to with following way:
NSInteger *pointerToProcessID = &yourNsintegervar;
If you look at the definition of NSInteger you'll see that it is a typedef for a simple integer. Basically, all the non-object types are stored as simple values, while the types that are complex objects are typically pointer properties. There are a couple reasons why these more complex objects are stored as pointers:
Using the value, itself, instead of the pointer would require copying (that is, if you use a pointer, you can put the object somewhere else and you only need to copy the much shorter address rather than all of the content that happens to be in that object, and hence it is more efficient that way).
When using a non-pointer type, it is necessary to know the required storage space, which works if you know the exact type of the object, but fails in the case of inheritance (e.g. an NSMutableArray may add additional fields to NSArray, for example. If you were to use NSArray instead of NSArray*, then assigning from an NSMutableArray would be broken, because the system would only have set aside enough space for the fields in the base class and not for the derived class. When using a pointer, however, since the pointer size is the same for both the base and derived types, one can assign the pointer for a derived type to a pointer to the base type, and still have things work correctly).
Note that it is possible and safe to use a pointer type with these primitive types, as well; however, this is not done for efficiency reasons (it would create additional allocation and dereferencing where not necessary).
As an aid to learning objective c/oop, I'm designing an iOS app to store periodic bodyweight measurements. I want to be able to retrieve the bodyweight in a variety of units (Kg, Lb, etc). For each bodyweight instance, can I/should I subclass NSNumber with a custom getter which return the weight in the correct unit? Perhaps I should simply subclass NSObject instead?
can I/should I subclass NSNumber with a custom getter which return the weight in the correct unit? Perhaps I should simply subclass NSObject instead?
Can you? Yes. Should you? No. NSNumber instances are actually never NSNumbers. NSNumber acts as a dispenser for it's various subclasses, which means that when you subclass it, you are essentially promising to reimplement the class dispension aspect of NSNumber (the same is true of NSArray and NSString).
Try to write a category on NSNumber rather than attempt to subclass it, and even then, NSNumber was never designed for manipulation and mathematical operations. Numbers are essentially immutable constructs, so you would be far better off writing a method that calculates things with primitives somewhere in your controller object. In your particular situation, NSNumber would only really be appropriate for persistence.
No, don't not use NSNumber at all, do not even add a category to it - this class (cluster) if designed for when you need to store a primitive type as an object and little else.
It you wish to encapsulate a weight write a class to do it, something along the lines of (code typed at terminal):
#interface Weight : NSObject
#property double kilos:
#property double pounds;
// etc
#end
#implementation Weight
{
double value; // stored in a suitable unit, kg, lb, oz, g, etc.
}
// implement getters and setters converting between unit of property and unit of value
// implement dependent property methods to setting, say, pounds produces a KVO
// notification for both pounds and kilos, etc. E.g.:
+ (NSSet *) keyPathsForValuesAffectingPounds
{
return [NSSet setWithObject:#"kilos"];
}
#end
Now you can set the value as one unit, read it as another, and get KVO notifications for all properties whenever one is set.
You'll want to add constructors (e.g. newWeightWithKilos:), maybe operations (e.g. addWeight: - which can just add the internal values), and need to decide whether a Weight is mutable or immutable.
You need not sub class the NSNumber instead you can subclass NSObject and add a property to set the weight in base unit(eg: kg's) and then you can add several methods that take the weight in base unit and returns the converter value in the units you specify(lb etc).
In C there is a default implementation of equality operator. Go through all the member and verify that they satisfy the equality operator. The default is somewhat stupid because if an object contains pointer then the equality operator of the member would be performed on the pointer.
Still, it's good enough for my purpose.
So does it?
Or are we expected to implement isEqual and the corresponding hash for everytime we create a custom object that may we want to use isequal for.
It seems to me the "default" implementation is to simply compare the pointer of the object and not it's member. Am I correct here? It's even worse than C++ standard comparison. That's what I want to verify.
It seems to me if our class is the immediate children of NSObject then isEqual will simply call it's parent's isEqual and that simply compare pointers.
Am I correct here? I am just wanting to make sure of that.
I think that NSObject’s implementation does pointer comparison, and various other classes from the SDK do what’s most appropriate, ie. NSString does comparison on string contents, NSArray compares content equality, and so on. If you want to have “better” equality defined for your custom objects, you have to decide about the semantics and implement it yourself.
Its a little confusing because of the way Apple separates their docs between protocols and interfaces.
#protocol NSObject
- (BOOL)isEqual:(id)object;
This is a required method to be implemented so NSObject (the class) definitely implements this although you wouldnt know it from looking at the class definition on apples dev site. This is directly from the headers in xcode.
In general without implementing a custom isEqual you will expect to only get pointer identity and thats ok in many cases. Systems need to be designed around the way you identify unique instances regardless of the peculiarity of a particular feature such as hash and isEqual. If you need to test for object equality beyond the pointer then you just have to do that.
As NSObject provides isEqual:, and all your objects are descendants of NSObject, then the the simple answer is that a default implementation is provided.
Now you are concerned over the algorithm this default uses, and in a comment write "I wouldn't be sure simply by testing". Let's look at testing, just for fun ;-)
Now isEqual: is a rather fundamental method, if Apple decided to change its semantics the consequences could be significant and not good. So while Apple is free to change how it is implemented provided the semantics remain the same, which means the same objects compare equal after the change as before. Now you've mentioned three possible algorithms isEqual: could use:
Pointer comparison - is it the exact same object
Shallow comparison - do the fields of the object have the same value compared directly
Deep comparison - do the non-pointer-valued fields compared directly have the same value, and do the pointer-valued fields compare equal using isEqual:
These all have different semantics, whichever one Apple has chosen it can't change without breaking a lot of code. And different semantics means you can test...
Coding as I type, errors expected! Only important bits included:
#implementation A
- (BOOL) isEqual:(id)other
{
NSLog(#"A.isEqual called");
return self == other; // true iff same object
}
#end
#interface B
#property (readwrite) int anInteger;
#property (readwrite) A *anA;
#end
#implementation B
#synthesize anInteger, anA;
#end
// Let's test the algorithm
A *myA = [A new];
B *bOne = [B new];
B *bTwo = [B new];
bOne.anInteger = 42;
bOne.anA = myA;
bTwo.anInteger = 42;
bTwo.anA = myA;
// What output is produced (all of it!)
NSLog(#"[bOne isEqual:bTwo] -> %#", [bOne isEqual:bTwo] ? #"Yes" : #"No");
HTH a little.
I have a game object which processed in two completely different places. In Contact Listener i check some conditions and if they occur i must save one or more portions of complex data. So i decided to use struct. For example:
struct SomeStruct
{
int value1;
int value2;
CGPoint value3;
b2Vec2 value4;
};
typedef SomeStruct SomeStruct;
In Game Scene i go through all game objects and if its the stack/array not empty, do some stuff and wipe it.
In Contact Listener it repeats from the beginning.
I must use this architecture because of strict order of execution (method must be called after other methods).
I suspect that i need something like vector or NSMutableArray (i think it will not work with struct), so vector may the the only way.
But don't understand how to achieve it. May you help me with some code/pseudocode or link to the book/article where i can found a solution?
Cocoa provides NSValue class for that purpose:
This creates an object that you can add to NSMutableArray:
NSValue *someObj = [NSValue valueWithBytes:&myStruct objCType:#encode(SomeStruct)];
You can use [someObj pointerValue] to access a void* representing the address of the structure that you put in NSValue.
There is a lot of solutions for this problem.
Don't use struct. An obj-c class is practically the same thing as a struct.
Use CFArray (CFArrayCreateMutable) and put it there as a pointer.
Use a C++ class with STL vector.
Use a C array (SomeStruct[]) and increase its length when you need it.
Use a classic implementation of a stack, with a linked list (every struct has a pointer to the next value).
I saw this thread but wanted to confirm:
How to convert NSNumber objects for computational purposes?
So basically anytime you want to deal with these objects you have to unpack their ivars, and then pack them back up into new objects, presumably NSNumbers?
That seems hella weak(and a large pain in the backside, no?).
How do you folks work with these?
Do you avoid them? Subclass them? is there mutable versions?
This just seems like a lot of work to deal with them, would love to hear their benefits and ways more experienced programmers have used them, or what tactics they have used to avoid using them.
Thanks,
Nick
So basically anytime you want to deal with these objects you have to unpack their ivars, and then pack them back up into new objects, presumably NSNumbers?
Yes. (By the way calling doubleValue does not just mean unpack the ivar. There maybe some conversions too.)
That seems hella weak(and a large pain in the backside, no?).
This "boxing" is necessary because primitive numbers by themselves to not support Objective-C (Foundation.framework)'s ref-counting scheme. For example, you have to box a number as NSNumber in order to store them in an NSArray.
Do you avoid them?
You can't.
Subclass them?
You shouldn't, but if you have to, follow how it's suggested:
As with any class cluster, if you create a subclass of NSNumber, you have to override the primitive methods of its superclass, NSValue. Furthermore, there is a restricted set of return values that your implementation of the NSValue method objCType can return, in order to take advantage of the abstract implementations of the non-primitive methods. The valid return values are “c”, “C”, “s”, “S”, “i”, “I”, “l”, “L”, “q”, “Q”, “f”, and “d”.
If all you want is add some convenient methods e.g. -numberByAddingNumber:, use a category:
#implementation NSNumber (MyExtension)
-(NSNumber*)numberByAddingNumber:(NSNumber*)another {
double myVal = [self doubleValue];
double anotherVal = [another doubleValue];
return [NSNumber numberWithDouble:myVal + anotherVal];
}
#end
...
NSNumber* a, *b;
...
NSNumber* c = [a numberByAddingNumber:b];
...
is there mutable versions?
No.
I avoid NSNumbers when I'm going to have to perform arithmetic on a variable. Actually, I avoid them at all times, unless I'm going to be rolling them into Core Data or something.
Now that there are Objective-C Literals in the newest version of clang compiler (version 3.2 up, came with Xcode 4.6 and also can be built from source), you can do stuff like #42 and #(7+35) to "box" NSNumbers.