I'm new to Objective-C, but experienced in C++ and C.
I want to store some enum constants in an array. In C++ I would do something like this:
enum color {RED, BLUE, YELLOW, GREEN};
vector<color> supportedColors;
supportedColors.push_back(RED);
supportedColors.push_back(GREEN);
But the NSArray will only store object pointers (id's). So how should they be stored? I could possibly cast them to integers and store them in an NSNumber object, but this seems messy.
I wonder what experienced obj-c programmers do?
Cast them to integers and store them in NSNumbers. :)
Native C types are really second class citizens in the Cocoa collection classes, and are often verbose to work with if you want to intermingle. C says that enums have integral values, so it's safe to use them as ints in this way.
Depending on what you're doing of course, you can simplify the manipulation code by wrapping that enum in an actual object ("MyColor") that has the enum as a property on it. These objects could then be tossed around in the collection classes as desired, with some upfront and runtime overhead that are unlikely to matter from a perf standpoint, depending on what you're doing.
You are perhaps looking for a way to simply loop through all of the options? How about just a plan old normal array?
typedef enum {RED,BLUE,GREEN,YELLOW} color;
color colors[4]={RED,YELLOW,GREEN,BLUE};
for (int i=0;i<4;i++)
colors[i];
On the other hand if performance isn't an issue and you are just looking to clean up the code a little; how about creating a class ColorArray that encapsulates NSMutableArray and creates the relevant methods.
ColorArray.h:
#import <Foundation/Foundation.h>
typedef enum {RED,BLUE,GREEN,YELLOW} Color;
#interface ColorArray : NSObject {
NSMutableArray* _array;
}
- (id) initWithArray:(Color[])colors;
- (void) addColor:(Color)color;
- (Color) colorAtIndex:(int)i;
#end
ColorArray.c:
#import "ColorArray.h"
#implementation ColorArray
- (id) init {
if (self = [super init]) {
_array = [[NSMutableArray alloc] init];
}
return self;
}
- (id) initWithArray:(Color[])colors {
if (self = [super init]) {
_array = [[NSMutableArray alloc] init];
for (int i=0;colors[i]!=0;i++)
[_array addObject:[NSNumber numberWithInt:colors[i]]];
}
return self;
}
- (void) dealloc {
[_array release];
[super dealloc];
}
- (void) addColor:(Color)color {
[_array addObject:[NSNumber numberWithInt:color]];
}
- (Color) colorAtIndex:(int)i {
return [[_array objectAtIndex:i] intValue];
}
#end
i would make a new class define the enum, and then instantiate supported colours as
vector<colorEnumClass> supportedColours
you can then do things like : give the class methods for testing a colour to see if it is a member of the vector .. then you can use that method in IF statements rather than explicit testing using relational operators. Clearer coding :-)
notice that the name supportedColours suggests that this vector should be a constant, defined at program start and never changed. If this is the case then the "colours" in the vector should be set in the constructor and never changed. The class should be implemented as a Singleton and you might even override the vector operators of pushback() etc to block modifications.
This is in the nature of the "new" Java enumeration technology
i should also mention that i am too new to Objective C to provide a syntactically correct code example .. sorry.
Related
I have a small function which I want to rewrite, so that function is valid for every class.
At the moment I have 10 of the same functions which all work same but every function is for another class.
I know, that I have to do it with reflections, but I am not so sure how to do it.
I already read this link:
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html
The functions I am talking about are:
-(NSCountedSet *)MissionGetReferecedNested:(id)modelObject
{
setOfObjects = [[NSCountedSet alloc]initWithArray:modelObject.MissionSectionList];
return setOfObjects;
}
-(NSCountedSet *)MissionGetSectionReferecedNested:(id)modelObject
{
setOfObjects = [[NSCountedSet alloc]initWithArray:modelObject.DamageAccountList];
return setOfObjects;
}
MissionSectionList and DamageAccountList are both NSMutableArrays from two different classes.
Is it possible to see if a class consists a NSMutableArray and if yes then it should call the .... modelObject.MyMutableArray?
You can use reflection like this:
- (NSCountedSet *)MissionGet:(id)modelObject
{
SEL propertySelector = NULL;
if ([modelObject respondsToSelector:#selector(MissionSectionList)]) {
propertySelector = #selector(MissionSectionList);
} else if ([modelObject respondsToSelector:#selector(DamageAccountList)]) {
propertySelector = #selector(DamageAccountList);
}
if (!propertySelector) {
[NSException raise:#"Invalid modelObject value" format:#"Model object %# does not contain any recognised selectors", modelObject];
}
return [[NSCountedSet alloc] initWithArray:[modelObject performSelector:propertySelector]];
}
But a more common technique among cocoa programmers would be:
- (NSCountedSet *)MissionGet:(id <MyCustomProtocol>)modelObject
{
return [[NSCountedSet alloc] initWithArray:[modelObject missionArray]];
}
Where you would accept any object which confirms to the protocol MyCustomProtocol. The protocol is defined in a header files somewhere, using:
#protocol MyCustomProtocol
#property (readonly) NSArray *missionArray;
#end
And then in each of your classes, declare it as implementing the protocol:
#interface MissionSectionListClass <MyCustomProtocol>
And add a method implementation:
#implementation MissionSectionListClass <MyCustomProtocol>
- (NSArray *)missionArray
{
return self.MissionSectionList;
}
#end
Using protocols is a bit more code, but it's the "right" way to go. It allows you to add support for new classes, without any change to your MissiongGet... method.
More info about protocols: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProtocols.html
EDIT : Cleared all my answer to this :
I think it's not possible to check if a class has a member variable of specified type. You can only check if a class has a specified method.
So, in this case it will be best if you make all your NSMutableArray list the same name, and then create a declared property for this list, and then do a respondsToSelector in your ...GetReferencedNested method.
So, for example, in all of your class create this property :
#property (nonatomic, retain) NSMutableArray * list;
and then in the ..MissionGetReferencedNested method :
if ([modelObject respondsToSelector:#selector(list)])
...
Correct me if i'm wrong...
In terms of style I'd also follow Abhi's suggestion.
But if you really want to inspect a class that you are stuck with and, for example build a NSCountedSet with the first NSMutableArray variable you can find, you could do it like this:
#import "Utilities.h"
#import <Foundation/Foundation.h>
#import <objc/objc-runtime.h>
#implementation Utilities
+ (NSCountedSet*)initCountedSetWithFirstArrayinObject:(id)someObject {
unsigned int c;
Ivar *ivar_arr = class_copyIvarList([someObject class], &c);
for (unsigned int i = 0; i < c; i++) {
if ([#"#\"NSMutableArray\"" isEqualToString:
[NSString stringWithCString:ivar_getTypeEncoding(ivar_arr[i]) encoding:NSUTF8StringEncoding]
]) {
return [[NSCountedSet alloc] initWithArray:object_getIvar(someObject, ivar_arr[i])];
}
}
return nil;
}
#end
Of course this has very limited real world use because it depends on you knowing that the first array will be the one you're interested in.
I think I have to go with the runtime type editing.(http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html)
The idea with the protocols was good but there I have to change a lot of things in the classes.(which is not possible/allowed) for me. My intension was only to change the functions so that I have only one function for all classes.
I think with the runtime type editing I can check what classes and attributes I have (?) Am I right?
Did somebody already work with runtime type editing?
i have some trouble writing a method in Objective-C to make an object nil. Here is some example :
#interface testA : NSObject
{
NSString *a;
}
#property (nonatomic, retain) NSString *a;
+(testA*)initWithA:(NSString *)aString;
-(void)displayA;
-(void)nillify;
#end
#implementation testA
#synthesize a;
+(testA*)initWithA:(NSString *)aString{
testA *tst=[[testA alloc] init];
tst.a=aString;
return [tst autorelease];
}
-(void)displayA{
NSLog(#"%#",self.a);
}
-(void)nillify{
self=nil;
}
- (void)dealloc {
[a release];
[super dealloc];
}
#end
int main(int argc, char **argv){
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
testA *test=[testA initWithA:#"some test"];
[test displayA];
test=nil;
//[test nillify];
NSLog(#"after setting to nil");
[test displayA];
[pool release];
return 0;
}
Apparently , when I set test object to nil and then call some method on it nothing happens , but if i call nillify instead of directly setting it to nil , displayA method works normally like test object is still there. Is there a workaround for nillify method to function properly ?
Your help is much appreciated !
You can't actually do something like this, because setting 'self' to nil only has any effect within the scope of that method (in your case, 'nilify'). You don't have any actual way to effect the values of pointers located on other parts of the stack or in random places in the heap, for example.
Basically any code that holds a reference to some object is responsible for maintaining and clearing those references itself. If you have some use case where random sections of code may need references to "live" objects of some kind, but where you'd want those object references to go away in response to some external event (maybe a user tracking system or something), you could do something with notifications, but the various modules tracking those "live" objects would still be responsible for listening for notifications and cleaning up references when they received them.
The 'nilify' thing, however, can't possibly work.
You cannot do what you're trying to do. self is just a local reference to an object that actually exists elsewhere. Setting it to nil doesn't mean anything. An object doesn't, in general, own itself, and it certainly doesn't control other objects' references to it. It's up to the owning objects to manage its lifetime.
There are a few things wrong with your code.
First, by convention, class names start with an uppercase letter. Please stick to these naming conventions as it will make it harder for other developers to work with your code (and even confuse you).
Next, your initWithName:... According to the naming conventions, a method with init in its name should be an instance method, not a class method. So either name it newWithName: or turn it into an instance method like this:
-(testA*)initWithA:(NSString *)aString{
self = [super init];
if (!self) return nil;
tst.a=aString;
return self;
}
If you keep it as class method (and name it newWithName:) you should not return a autoreleased object since according to the naming conventions method that start with init... or new... return a retained object. If you do not follow these conventions, the static analyzer will give you "false" warnings and it will become useless for you.
Now for the reason your nillify doesn't work: the self is in fact an argument to a method. Under the hood, your nillify method actually has two arguments that you do not see: the self pointer and the selector pointer. This means, self is actually a variable on the stack. And if you overwrite it, you only overwrite that stack variable but that doesn't influence your test variable which is somewhere else.
As an example, consider a method - (void)foo:(NSString *)bar;. The compiler turns it into the equivalent of the C function (void) foo(id self, SEL _cmd, NSString *bar).
I have a function That takes by reference any kind of object
-(BOOL)RemoteCall:(id**)DataClass;
in the implementation i use [*DataClass isMemberOfClass:[NSMutableArray class] to find out the type of the object. The problem is it does not work with NSMUtableArrays Does anybody have a solution to this problem ? Here is the relevant code:
Implementation:
-(BOOL)RemoteCall:(id**)DataClass
{
if([*DataClass isMemberOfClass:[NSMutableArray class] ] == YES)
{
NSMutableArray * SW =(NSMutableArray *)*DataClass;
//do something with SW
DataClass= (id**)SW;
return TRUE;
}
}
Any help and I mean anything at all will be appreciated, I'm stuck.
Method Call:
NSMutableArray * channelArray = [[NSMutableArray alloc]init]
Services * serv = [[Services alloc] init];
return [serv RemoteCall:&channelArray];
Pass by reference in Objective-C is almost never the right way.
There are a number of problems with that code.
(id**) is a pointer to a pointer to a pointer to an object. Probably not at all what you want.
YES and NO are BOOL return types; not TRUE
there is no reason in that code to be returning something by reference.
method names start with lower case letters. Arguments do, too.
There will never be an instance of NSMutableArray in an application; just subclasses
You can't tell the difference between a mutable and immutable array in the first place; check for isKindOfClass: or isMemberOfClass: for an NSMutableArray won't do you much good (it is useful, but misleading).
This is better:
-(BOOL)remoteCall: (id) dataThing
{
if([dataThing isKindOfClass:[NSMutableArray class]] == YES)
{
NSMutableArray *swArray = dataThing; // not strictly necessary, but good defensive practice
//do something with swArray
return YES;
}
return NO;
}
To be called like:
NSMutableArray * channelArray = [[NSMutableArray alloc]init]; // you'll need to release this somewhere
Services * serv = [[Services alloc] init];
return [serv remoteCall:channelArray];
Since you don't return a different array in remoteCall:, channelArray's contents will be manipulated by the method and the YES/NO return value.
If there is some reason why the above seemingly won't work for you, please explain why.
Note: The code obviously requires an NSMutableArray if you are going to muck with the contents. The isKindOfClass: could be checking for NSMutableArray or NSArray and it wouldn't matter either way. When using arrays in your code and requiring a mutable array, it is up to you to make sure the data flow is correct such that you don't end up w/an immutable array where you need a mutable array.
If you don't need to reassign your variable, then don't use this. id or NSObject * is just fine and works by reference anyway. id * or NSObject ** would be references. id ** doesn't make sense at all here.
Also, learn naming conventions (like upper/lowercase).
NSArray is a class cluster. That means that every NSArray instance is actually an instance of some subclass. Only isKindOfClass: is useful for class-membership testing with class clusters.
Also... thats horrible code - please accept this:
-(BOOL)remoteCall:(id)dataClass {
if([dataClass isKindOfClass:[NSMutableArray class]]) {
NSMutableArray *sw =(NSMutableArray *)dataClass;
return YES;
}
}
that should work.
Constructive critisism of coding: You need to adhere to coding conventions. Although your code will work... its not brilliant to read and theres a lot of unnecessary *s and such.
Function names should be camel coded with a preceeding lower-case letter as should variable names. Passing (id) into a function doesn't require *s at all. Objects you pass into a function only available throughout the scope of the method anyway and that method doesn't own it, I'm not sure what you're trying to do with all the extra *s, but just treat objects you pass into the method as if you don't own them. :)
As Eiko said before, i'd use just id and not double pointers to ID.
I'm also pretty sure that isMemberOfClass is your Problem. isMember does not check for inheritance, so you're only asking for Top level Classes. isKindOfClass is probably the better choice, as there is no guarantee that Apple doesn't use an internal subclass of NSMutableArray internally. Check the Apple Docs.
i'd write it as such:
-(BOOL)RemoteCall:(id)dataClass
{
if([dataClass isKindOfClass:[NSMutableArray class] ] == YES)
{
NSMutableArray * SW =(NSMutableArray *)dataClass;
//do something with SW
return TRUE;
}
}
Alright, so I think I'm doing this the right way. I'm new to objective-C, so I'm not sure about the syntax... I have a set of code that I need to call multiple times, from different files. So I made a new class that has a method in it that I'll call and pass it the values that it needs.
Because I am passing different values I've put them in a dictionary and decided to just pass the dictionary. Here is that code:
NSNumber *testNum = [NSNumber numberWithInt:varMoney];
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
[dictionary setObject:#"OMG, Object 1!!!!" forKey:#"1"];
[dictionary setObject:#"Number two!" forKey:#"2"];
[dictionary setObject:testNum forKey:#"3"];
This code creates a test variable, and then puts it into the dictionary "dictionary." That all works, I have my nice little dictionary. However, now I need to create the class and it's method that will recieve the dictionary, and do something with it.
This is my class header file:
#import <UIKit/UIKit.h>
#interface EndOfTurnObjC : UIView {
}
#end
And this is the implementation file:
#import "EndOfTurnObjC.h"
#implementation EndOfTurnObjC
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
}
return self;
}
- (void)dealloc {
[super dealloc];
}
#end
I haven't created any of the real code, because I'm not sure how to do the passing. I need to create a function (Method?) in the class that will take a Dictionary has a parameter, and then return the dictionary.
I also have no idea how to call such a function because it's in the class. So, the questions are:
1: How do I define the method in the class to accept the dictionary as a parameter (and then perhaps some example code to pull out one of the objects in a dictionary, so I can be sure it works)
2: How do I return the dictionary at the end of the method?
3: How do I call this method, in the class, from another class? (I know it involves making an object of thing class and calling the method of the object... I think, but I'm not sure about the syntax.)
Please include relavent code for the 3 files (header, implementation, and the other class that I call from). Thank you so much, I've been working on this particular problem for a while now.
Apple's The Objective-C Programming Language is a good and pretty concise reference for Objective-C syntax. What you want is just a normal method that takes an NSDictionary as a parameter. So as given in that document:
A message with a single argument affixes a colon (:) to the selector name and puts the argument right after the colon. This construct is called a keyword; a keyword ends with a colon, and an argument follows the colon, as shown in this example:
[myRectangle setWidth:20.0];
So a method call to pass dictionary would look like:
[someObject setAttributes:dictionary];
In the header:
-(NSMutableDictionary *) doSomethingWithDictionary:(NSMutableDictionary *) aDict;
in the implementation:
-(NSMutableDictionary *) doSomethingWithDictionary:(NSMutableDictionary *) aDict{
//do something with the dictionary
return aDict;
}
To call the method:
NSMutableDictionary *returnDict=[EndOfTurnObjC doSomethingWithDictionary:dictionary];
Note that as a matter of good design you wouldn't want to pass a mutable dictionary around like a token. That is asking for trouble. Instead pass static dictionaries and get another dictionary back.
You also shouldn't be passing data to a UIView. Instead, your UIViewController should process the data and then populate the view's UI elements as needed.
if you just want to do stuff to your dictionary u just
-(void) changeMyDictionary:(NSMutableDictionary * ) dictionary_
{
[dictionary_ doStuff];
....
...
}
no need to return the dictionary.
I created an "SDMutableGrid" class so that I could use a grid. It's just a child of NSMutableArray that contains a number for arrays equal to the number of rows in the grid.
Currently, the program quits before it really starts and it appears that it is because the methods defined for NSMutableArray somehow do not apply to SDMutableGrid, anyone know why?
Here is the .h :
#import <Foundation/Foundation.h>
#import "SDDimensions.h"
#interface SDMutableGrid : NSMutableArray {
SDDimensions dimensions;
}
#property (nonatomic) SDDimensions dimensions;
- (id)initWithDimensions:(SDDimensions)newDimensions;
- (void)addObject:(id)anObject toRow:(NSUInteger)row;
#end
Here is the .m :
#import "SDMutableGrid.h"
#implementation SDMutableGrid
#synthesize dimensions;
- (void)setDimensions:(SDDimensions)newDimensions {
if (newDimensions.width < dimensions.width) {
NSMutableArray *anArray;
NSRange aRange = NSMakeRange(newDimensions.width, dimensions.width - newDimensions.width);
for (NSUInteger i = 0; i < MIN(dimensions.height,newDimensions.height); i++) {
anArray = [self objectAtIndex:i];
[anArray removeObjectsInRange:aRange];
}
}
dimensions.width = newDimensions.width;
if (newDimensions.height > dimensions.height) {
for (NSUInteger i = dimensions.height; i < newDimensions.height; i++) {
[self addObject:[[NSMutableArray alloc] initWithCapacity:dimensions.width]];
}
} else if (newDimensions.height < dimensions.height) {
[self removeObjectsInRange:NSMakeRange(newDimensions.height, dimensions.height - newDimensions.height)];
}
dimensions.height = newDimensions.height;
}
- (id)initWithDimensions:(SDDimensions)newDimensions {
if (self = [super initWithCapacity:newDimensions.height]) {
NSMutableArray *anArray;
for (NSUInteger i = 0; i < newDimensions.height; i++) {
anArray = [[NSMutableArray alloc] initWithCapacity:newDimensions.width];
NSLog(#"Got this far");
[self addObject:anArray];
NSLog(#"woot");
[anArray release];
}
NSLog(#"Finished Initializing grid");
}
return self;
}
- (void)addObject:(id)anObject toRow:(NSUInteger)row {
[[self objectAtIndex:row] addObject:anObject];
}
#end
And here is what is appearing on the console:
2009-08-12 15:27:02.076 Flipswitch[1756:20b] Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: ' -[NSMutableArray initWithCapacity:]: method only defined for abstract class. Define -[SDMutableGrid initWithCapacity:]!'
2009-08-12 15:27:02.080 Flipswitch[1756:20b] Stack: (
807902715,
2536648251,
808283725,
808264737,
13690,
11018,
10185,
814713539,
814750709,
814739251,
814722434,
814748641,
839148405,
807687520,
807683624,
814715661,
814752238,
10052,
9906
)
The short, easy answer: Don't make a subclass of NSArray. It's better to make a category on NSArray or make an NSObject subclass that has an NSArray ivar that you talk to.
The long, technical answer: NSArray is a class cluster. This means that it isn't actually one class, but many classes operating under the NSArray abstract class interface that are each implemented in a different way (say, one implementation for small arrays, another for big arrays, etc.). To create a subclass of a class cluster, you have to implement all the primitive methods of the abstract class you are inheriting from, manage your own storage and basically reimplement all the stuff you were hoping to get for free by subclassing.
More simply, you could just create a category if you don't require additional ivars. If you want an object that behaves like an array with additional state, you can create a class that has an NSArray and use Objective-C message forwarding to forward everything except your custom behavior to that class.
This is due to the nature of 'Class Clusters' used for collection classes in Foundation.
See:
Class Clusters
Basically, NSMutableArray defines a public interface to 'mutable arrays', but is not the actual class you use when initialized. So 'initWithCapacity:' is defined, but not implemented in NSMutableArray. If you run:
NSMutableArary *foo = [[NSMutableArray alloc] init];
NSLog(#"%#", [foo className]);
you will print "_NSCFArray", which is a concrete subclass of NSMutableArray (and NSArray). To work around this, I would have a instance variable that is an NSMutableArray, or implement 'initWithCapacity:' with a suitable meaning (such as a capaciy of '3' means a 3x3 grid).
Ok, I found the answer from this question
Although the questions are different, the answer is the same and that is that due to the setup of NSArray (and therefore NSMutableArray), you cannot subclass it without implementing the methods yourself.
So I guess I'll just make SDMutableGrid have an NSMutableArray variable instead of actually being an NSMutableArray.
You problem is that you are not implementing abstract methods of NSMutableArray super class that need to be implemented, it says
-[NSMutableArray initWithCapacity:]: method only defined for abstract class. Define -[SDMutableGrid initWithCapacity:]!' 2009-08-12 15:27:02.080 Flipswitch[1756:20b]
So you need do define initWithCapacity in your subclass, I would recommend to not extend NSMutableArray, there is no need, just make a class that has a mutable array in it.