How to set retain for properties nested to struct? - objective-c

There is really something in objc I cannot understand. I know how to make retainable properties but cannot understand how to make retainable "sub" struc fields which are not "public" properties but only "private" properties (the private properties are only managed by my methods).
Here is my code:
struct _device_descriptor {
NSInteger accessoryNumber; // Accessory the device belongs to
NSInteger slotNumber; // Slot number used for the device
NSString* slotName; // Slot name
};
typedef struct _device_descriptor device_descriptor_t;
#define NUMBER_MAX_CARD_READERS 10
#define NUMBER_MAX_ACCESSORIES 10
#interface CardDeviceManager : NSObject {
// Card devices (among accessories)
NSInteger m_numberOfCardDevices; // Number of devices.
NSMutableArray* m_accessoryList; // List of all accessories
#private
// Accessories
NSInteger m_numberOfAccessories; // Number of accessories
NSInteger m_numberOfAcceptedAccessories;
device_descriptor_t m_cardDevice[NUMBER_MAX_CARD_READERS]; // Array of card devices.
}
I want the slot name (slotName in the struct) to be retained each time I assign it in my methods, but this is not a property since not visible from outside.
For example, each time I initialize it with another NSString, I do this:
//_tmpName is a NSString
// Warning: slotName must be released later since we retain it.
m_cardDevice[i].slotName = [[NSString stringWithString: _tmpName] retain];
I really feel this is not a "good" (not elegant) way of doing.
I think I should remove the _device_descriptor struct and have something like this:
NSInteger accessoryNumber[NUMBER_MAX_CARD_READERS]; // Accessory the device belongs to
NSInteger slotNumber[NUMBER_MAX_CARD_READERS]; // Slot number used for the device
NSString* slotName[NUMBER_MAX_CARD_READERS];
But this is not better since I do not gather common things in a struct...
Is there a smarter way ?
Regards,
Franz

You're right; you can't do this via property syntax. I'd probably write C functions for getting and setting the struct fields, and then just make an arbitrary rule that accessing data from the structs in any way other than the functions is improper.
For example, I'd have:
device_descriptor_t* DeviceDescriptorCreate(NSInteger accessoryNumber,
NSInteger slotNumber,
NSString* slotName); //malloc()
void DeviceDescriptorDestroy(device_descriptor_t* device); //free()
void DeviceDescriptorSetSlotName(device_descriptor_t* device, NSString* slotName); //release old value, set new one
NSString* DeviceDescriptorGetSlotName(device_descriptor_t* device);
Et cetera.

Related

Multidimensional array of NSInteger as instance variable

I'm attempting to understand Objective C arrays. What I want to do is create a multidimensional NSInteger instance array.
Does the code immediately below create an array equivalent to initiating an array and running a double for loop containing NSNull at each point?
#interface Builder: UIView {
NSInteger superDuperArray[5][4];
}
How do I add/replace specific data (NSIntegers) considering this is not an NSMutableArray? I need to do so elsewhere in the file, in several different methods.
The instance variable you've declared does indeed create a multidimensional array that is accessible from any method in the object. It does not, however, contain NSNulls, because that's an object and NSInteger is a primitive type. It contains 0 in every slot (all ivars are initialized to their appropriate zero-value at the object's allocation).
If you don't need to change the size of the array, you're pretty much done. You access the array inside your object just like you'd access an array anywhere else, by subscripting: superDuperArray[2][1], e.g.
Here's a full sample:
#import <Foundation/Foundation.h>
#interface Mutli : NSObject
- (NSInteger)numberAtRow:(NSInteger)row column:(NSInteger)col;
- (void)setNumber:(NSInteger)newNum atRow:(NSInteger)row column:(NSInteger)col;
#end
#implementation Mutli
{
NSInteger numbers[5][4];
}
-(void)setNumber:(NSInteger)newNum atRow:(NSInteger)row column:(NSInteger)col
{
numbers[row][col] = newNum;
}
- (NSInteger)numberAtRow:(NSInteger)row column:(NSInteger)col
{
return numbers[row][col];
}
#end
int main(int argc, const char * argv[])
{
#autoreleasepool {
Mutli * m = [Mutli new];
[m setNumber:10 atRow:2 column:1];
NSLog(#"%ld", [m numberAtRow:2 column:1]);
// This may not crash!
[m setNumber:10 atRow:100 column:34];
}
return 0;
}
N.B. That last line. There's no bounds checking inherent to primitive arrays. Accesses outside the bounds you've set may not crash or cause any immediately-noticeable problem. Instead, you'll get garbage for reads, and will corrupt memory when you write. (Technically, you're invoking Undefined Behavior.) You should really include bounds checking in your accessor methods.
Also, a style note: it's no longer good ObjC form to put ivars in an object's interface. Hide it in the #implementation block as I've done.
An NSInteger is simply a C 'int' (or 64-bit long on 64-bit platforms - the size is immaterial though).
So what you've declared is nothing but a C array, of integers. Since they're simple integers, there's no NSNull.
If you'd declared an NSArray (of NSArray for 2D) then you'd be looking at NSNull and NSNumber in the elements.

Building Constructors for RPG game in Objective C

I have a character model class which has this structure:
#interface CharacterModel : NSObject
{
// parent of this character
CharacterModel *parentChar;
// basic details
NSString *fname, *sname, *nick;
NSString *char_type; // categories of characters: dwarf, etc
// health
int health;
// cash
double cash;
double graft;
// flags
bool is_cop, is_player, is_ai, is_playable;
// Skills
int skill_speed;
int skill_stamina;
int skill_aggr;
int skill_another;
int skill_somethingelse;
// Total = 100
// Hidden RPG skills
int corruption;
int greed;
// Rep skills
int reputation;
// Misc. flags
int active, picked, is_locked;
}
The problem are 2 things.
1) I would need to re-write this structure in the #property (nonotomic)... part of the .h file, and I would need to do it again for #synthesize part of the .m file
Is there a way to reduce the need to re-write stuff; can I put all this in a struct or something and then just #synthesize that?
2) The constructor will have a stupidly long function name.
I really, really do not want to be writing a constructor that has hundreds of variables/fields.
ie:
-(id)initCharacter:(NSString *)name, and every other class variable mentioned above ...
Is there are a way around this?
I was thinking of doing a NSMutableDictionary, but you would STILL need to write a constructor with every field you want somewhere.
Any help on this would be great.
Thanks
I really think you should take your design one step further. It is very inflexible to have explicitly defined all the skills and flags like that. Consider creating new classes called:
Skill
Attribute
Flag
Your character class will then have:
NSMutableArray* skills;
NSMutableArray* attributes;
NSMUtableArray* flags;
and obviously getters/setters and add/remove methods for each.
Not only will it make your class look neater but it will also save you a lot of typing.
If you can use the modern Objective-C runtime (available on the iPhone and for 64 bit OS X programs) you will have to write the property definitions twice. Just write the #property and the #synthesize parts, the instance variables will be created automatically. If you need to support the old runtime you just have to write it three times, there is nothing you can do about it.
To the constructor you probably should not pass values for every property. Initialize them to some sensible default values. Note that you don’t have to do anything if the default value is 0, 0.0, nil or NULL - alloc makes sure that all ivars are initialized to zero.
If you want to set all the properties from a NSDictionary you can use key-value-coding to set them instead of doing this manually:
for (NSString *key in dictionary) {
id value = [dictionary objectForKey: key];
[self setValue: value forKey: key];
}

How to write a NSMutableArray of a struct type that also includes NSMutableArray inside of it?

I was wondering if there is any sample code out there for objective-C for implementing a NSMutableArray of type struct. Inside, I need there to be 2 mutable arrays (via NSMutableArray also) declared in the struct. All the code samples in my book show me how to make an array of defined size via C array syntax (with the brackets), but I don't know how to get one going with NSMutableArray. Has anyone else done this before? Here's my code so far...It compiles fine I have defined the size of the arrays (2 and 5 are used in my code below as an example, but I need to set it so I can have them mutable. I can work with simple structs when they just have some of the "easier-to-understand" data types like int, double, short, long, BOOL (you get the idea). When it gets into pointers though, this is where I become lost (I can use pointers fine, but knowing how to put them in a struct is the difficult part). Once the code is working with NSMutableArray's, would I put "network" in the interface as a pointer to type "Network"? I tried this before, but I got errors. In the end, I basically want to be able to write
network.input[2].list[1].value = 5.0;
on an arbitrarily defined array of type "Network". Could anyone offer suggestion or links to information about making a NSMutableArray of type "Network" which includes a struct of two NSMutableArray's? Thanks for any help!
SomeFile.h
#import <Foundation/Foundation.h>
struct lists{
double value;
};
// supporting structs
struct inputs{
struct lists list[2];
};
struct Network {
struct inputs input[5];
struct lists output[5];
}
#interface SomeFile : NSObject {
}
#end
SomeFile.m
#import "SomeFile.h"
#implementation SomeFile
#end
NSArray and NSMutableArray can only contain Objective-C objects, so you can't store structs in them. If the contents must be structs and you want something similar to NSArray, use NSPointerArray, available in 10.5 and later.
You can store pointers to Objective-C objects (like NSPointerArray* or id) inside a struct just like any other pointer type. For example, you could declare a struct for a doubly-linked list node that stores an Objective-C object like this:
typedef struct DoublyLinkedListNode {
id object;
__strong struct DoublyLinkedListNode *next;
__strong struct DoublyLinkedListNode *prev;
} DoublyLinkedListNode;
The __strong attribute is used in connection with garbage collection in Objective-C 2.0, since pointers to Objective-C objects act as strong references, but C pointer types do not by default. This way, as long as one node in the list is referenced from a __strong reference, the list won't disappear. (Read the Garbage Collection Programming Guide for details, and particularly the second half of Using Core Foundation with Garbage Collection.) You'll probably want to consider doing this for your structs.
As far as your desired syntax, I may not have fully understood your question, but you won't be able to use the bracket syntax to access objects in a Cocoa collections like an NSPointerArray. (Also, odds are you'll have to use the "->" operator instead of "." for the structs, since they're likely to be allocated on the heap. All Objective-C objects must be, and I assume you'll want to store these structs outside of the local scope of a method.)
Since Objective-C doesn't have generics, you also can't "implement [an] NSMutableArray of type struct". In fact, one of your previous SO questions has more detail on the subject. If that's not what you meant, feel free to clarify.
This is not be a complete answer; I’m not sure what you mean by not knowing how to put pointers in structs. I’m going to proceed by assuming you want to model an network of multiple inputs and outputs with a dynamic number of both.
You have a few choices here:
Use value objects instead of structs to store your values:
[[[network inputs] objectAtIndex:2] replaceObjectAtIndex:1 withObject:[NSNumber numberWithDouble:5.0]];
Model your Network with an object:
#interface Network : NSObject {
// ivars
}
- (void) setInput:(double)value atIndex:(NSInteger)valueIndex ofInputAtIndex:(NSInteger)inputIndex;
- (double) outputAtIndex:(NSInteger)index;
#end
Just use structs like you’re already doing; if you need to change the size up-front, use your friend malloc:
struct Network_value {
double value;
}
struct Network {
struct Network_value **inputs;
struct Network_value *outputs;
};
void Network_alloc(Network *n, unsigned inputs, unsigned input_values, unsigned outputs) {
n->outputs = calloc(sizeof(Network_value), outputs);
n->inputs = calloc(sizeof(Network_value *), inputs);
while (inputs --> 0) {
n->inputs[inputs] = calloc(sizeof(Network_value), input_values);
}
}
void Network_free(Network *n, unsigned inputs) {
free(n->outputs);
while (inputs --> 0) {
free(n->inputs[inputs]);
}
free(n->inputs);
}
Network network;
Network_alloc(&network, 5, 2, 5);
network.inputs[2][1].value = 5.0;
Network_free(&network, 2);
Combine ideas 2 and 3 by presenting a Network object but internally store the values with structs. This is probably a good idea if the number of inputs and outputs is very large.

How to detect a property return type in Objective-C

I have an object in objective-c at runtime, from which I only know the KVC key and I need to detect the return value type (e.g. I need to know if its an NSArray or NSMutableArray) of this property, how can I do that?
You're talking about runtime property introspection, which happens to be something that Objective-C is very good at.
In the case you describe, I'm assuming you have a class like this:
#interface MyClass
{
NSArray * stuff;
}
#property (retain) NSArray * stuff;
#end
Which gets encoded in XML something like this:
<class>
<name>MyClass</name>
<key>stuff</key>
</class>
From this information, you want to recreate the class and also give it an appropriate value for stuff.
Here's how it might look:
#import <objc/runtime.h>
// ...
Class objectClass; // read from XML (equal to MyClass)
NSString * accessorKey; // read from XML (equals #"stuff")
objc_property_t theProperty =
class_getProperty(objectClass, accessorKey.UTF8String);
const char * propertyAttrs = property_getAttributes(theProperty);
// at this point, propertyAttrs is equal to: T#"NSArray",&,Vstuff
// thanks to Jason Coco for providing the correct string
// ... code to assign the property based on this information
Apple's documentation (linked above) has all of the dirty details about what you can expect to see in propertyAttrs.
Cheap answer: use the NSObject+Properties source here.
It implements the same methodology described above.
The preferred way is to use the methods defined in the NSObject Protocol.
Specifically, to determine if something is either an instance of a class or of a subclass of that class, you use -isKindOfClass:. To determine if something is an instance of a particular class, and only that class (ie: not a subclass), use -isMemberOfClass:
So, for your case, you'd want to do something like this:
// Using -isKindOfClass since NSMutableArray subclasses should probably
// be handled by the NSMutableArray code, not the NSArray code
if ([anObject isKindOfClass:NSMutableArray.class]) {
// Stuff for NSMutableArray here
} else if ([anObject isKindOfClass:NSArray.class]) {
// Stuff for NSArray here
// If you know for certain that anObject can only be
// an NSArray or NSMutableArray, you could of course
// just make this an else statement.
}
This is really a comment addressing an issue raised by Greg Maletic in response to answer provided by e.James 21APR09.
Agreed that Objective-C could use a better implementation for getting these attributes.
Below is a method I quickly threw together to retrieve attributes of a single object property:
- (NSArray*) attributesOfProp:(NSString*)propName ofObj:(id)obj{
objc_property_t prop = class_getProperty(obj.class, propName.UTF8String);
if (!prop) {
// doesn't exist for object
return nil;
}
const char * propAttr = property_getAttributes(prop);
NSString *propString = [NSString stringWithUTF8String:propAttr];
NSArray *attrArray = [propString componentsSeparatedByString:#","];
return attrArray;
}
Partial list of attribute keys:
R Read-only
C Copy of last value assigned
& Reference to last value assigned
N Nonatomic property
W Weak reference
Full list at Apple
You can use isKindOfClass message
if([something isKindOfClass:NSArray.class])
[somethingElse action];
If you know that the property is defined :
id vfk = [object valueForKey:propertyName];
Class vfkClass = vfk.class;
And compare with isKindOfClass, isSubClass, etc.

How to build a NSArray (or NSMutableArray) of class methods in Objective-C?

I'm trying to build a NSArray of methods in Objective-C.
(What I'm trying to accomplish here is something like the following in C)
typedef (void)(*handler)(int command);
void handleCommandA(void) { ... }
void handleCommandB(void) { ... }
static const handler handler_table[10] = {
handleCommandA, handleCommandB, handleCommandC
};
I have to port this to Objective-C and I don't know how to
build an array of function pointers (in Objective-c world,
class methods) at compile-time.
In Objective-C I have the following.
- (void)handleCommandA { ... }
- (void)handleCommandB { ... }
/* Now how to add above 2 functions into NSArray? */
NSArray *handler_table = [NSArray arrayWithObjects:... ]; /* This doesn't seem to work. */
The problem here is that to bind those functions you must use the selector keyword which returns a SEL type. This is a pointer type whereas NSArray stores objects.
You thus have three options;
Use a regular C-type array
Fold the functions into an NSObject derived class that will call them.
Use a protocol.
The second is likely the nicer and for this you can use the NSValue class to hold the selector results. E.g;
NSValue* selCommandA = [NSValue valueWithPointer:#selector(handleCommandA:)];
NSValue* selCommandB = [NSValue valueWithPointer:#selector(handleCommandB:)];
NSArray *handler_table = [NSArray arrayWithObjects:selCommandA, selCommandB, nil ];
When you have retrieved the correct entry from the array, to convert back you would do;
SEL mySelector = [selCommand pointerValue];
[someObject performSelector:mySelector];
(Note I'm assuming that from your objective-c syntax that these are intended to be used as methods on an object and not global functions. If you wish to use them globally then you should write them as you would in plain C.)
Another option is to formalize the command methods into a protocol. This allows you to write functionality that will work on any object which implements that protocol and the compiler will provide more checking than if you were just calling selectors.
E.g.
// some header
#protocol CommandHandler
#required
-(void) handleCommandA;
-(void) handleCommandB;
#end
// some other header
#interface someClass : NSObject<CommandHandler>
{
// you will receive compiler warnings if you do not implement the protocol functions
}
Your handling and dispatch code is then written to work with objects of type "CommandHandler". E.g
-(void) registerForCommands:(CommandHandler*)handler
Use NSValue.
For example:
NSArray* handlers = [NSArray arrayWithObjects:[NSValue valueWithPointer:handleA] ... ];
then to access :
handleptr* handle = (handlerptr*)[[handlers objectAtIndex:0] pointerValue];
handle(foo_bar);
In Objective-C, you don't pass around methods; you pass around selectors, which are basically the canonical names of methods. Then, to make an object respond to a selector message, you send it performSelector:. For example:
NSString *exampleString = [NSString stringWithString:#"Hello"];
SEL methodName = #selector(stringByAppendingString:);
// ^This is the selector. Note that it just represents the name of a
// message, and doesn't specify any class or implementation
NSString *combinedString = [exampleString performSelector:methodName withObject:#" world!"];
What you'll want is to make an array of NSStrings containing the names of the selectors you're interested in. You can use the function NSStringFromSelector() to do this. Then, when you want to use them, call NSSelectorFromString() on the strings to get the original selector back and pass it to the appropriate object's performSelector:. (As shown in the example above, the receiver isn't encoded in a selector — just the method name — so you might need to store the receiver as well.)