I need a string->string mapping to be used at runtime (think NSDictionary), except the mapping will NEVER change after build-time.
The naive solution is to just use an NSDictionary, but there has to be a more optimal way to do this, no?
Optimal in the sense that if the mapping is known at compile-time, and known to never change, the compiler should be able to do the mapping at compile-time. An NSDictionary needs to do a hash lookup at runtime. I know it's constant time, but it just feels a bit "unclean" to me...
You could hard code your NSDictionary if that isn't too cumbersome (i.e. it's not huge), or you could create a plist and include it in your app bundle. Then at app launch, read the dictionary from the plist (a couple lines of code). Each of these approaches is about the same effort. The advantage of using a plist is that if you have to change it, you are editing the plist, not code.
A static NSDictionary is the right tool for this. You typically initialize these with an +initialize method:
static NSDictionary *kDictionary;
+ (void)initialize {
if (self == [MYClass class]) {
kDictionary = [[NSDictionary alloc] initWith...];
}
}
initialize is called one time per class, with thread safety, immediately before the first requested method is called on that class (usually this first method is +alloc). The self test is because subclasses will automatically call their [super initialize], and you generally don't want to run this more than once in that case.
Related
I've been writing C code for quite some time but am very new to Objective-C and OO program design. I am having trouble wrapping my head around how to design my program and it's classes -- my standby procedural programming paradigm of passing an opaque pointer to everything just won't die a quiet death. I am writing a simple program in Xcode4 in Objective-C and I have some questions on design and convention. I've designed two classes "PacketCapture" and "IPGeo". The first is a light wrapper around libpcap with some added parsing logic and the second is a light wrapper around MaxMind's GeoIP C API. In my main module, I instantiate the PacketCapture class and pass control to the packet capture loop:
int main(int argc, const char *argv[])
{
#autoreleasepool
{
PacketCapture *_PacketCapture = [[PacketCapture alloc] init:"en1" withFilter:""];
if (_PacketCapture == nil)
{
NSLog(#"Can't instantiate PacketCapture class");
return (-1);
}
IPGeo *_IPGeo = [[IPGeo alloc] init:"/usr/local/share/GeoIP/GeoIPCity.dat"];
if (_IPGeo == nil)
{
NSLog(#"Can't instantiate IPGeo class");
return (-1);
}
NSLog(#"entering packet loop...");
[_PacketCapture packetCapture];
}
return (0);
}
Inside of the packetCapture method a parsing function is called that chops up the packet and displays it. I need to use the GeoIP logic down inside the parsing code to lookup the IP addresses against the geo database. Therein lies my problem and general confusion.
My questions:
Where should I instantiate the GeoIP class?
Is having an infinite loop inside an object in a class bad OO design? Feels like it could be. Perhaps if I restructure this problem will be easily solved?
Thanks for any help.
There are a few concerns with this code:
You are using reserved identifiers (any identifier beginning with two underscores or an underscore followed by an uppercase letter are reserved). As I mentioned in the comment, it's unlikely to cause any real issues but it is something to consider.
A more “Cocoa” approach for initialiser methods is to accept either a filename or an NSData object containing the contents of the file. The latter has the benefit that the data does not necessarily have to be sourced from a file on a disk. For example:
NSData *geoData = [NSData dataWithContentsOfFile:#"/path/to/file"];
IPGeo *ipGeo = [[IPGeo alloc] initWithData:geoData];
In the initWithData: method, you would either parse the data as raw bytes, or perhaps convert it to a string and parse that, etc. In any case, init: is not a very good name for a method because it is not clear what the method expects. If you still want it to accept a filename, a good name may be initWithContentsOfFile: (and have it accept an NSString rather than just a const char *).
Likewise, with your PacketCapture initialiser method. Objective-C gives you the benefit of being able to name your arguments and this allows for semi-self-documenting code, so method naming is incredibly important in Objective-C, if only to remain consistent with all other commonly used frameworks.
There is nothing wrong with implementing an infinite loop in a method, this is more-or-less what NSApplication does with its run method. There are alternatives to this, such as threading, or using run loops, etc, but for small command-line utilities there is seldom any benefit. I would suggest expanding and renaming your method just a little bit:
// Methods are usually named as if you are telling the object what to do
[packetCapture capturePackets];
// or, if you think this might be of some benefit:
NSDate *stopTime = /* specify a time to stop */;
[packetCapture capturePacketsUntilDate:stopTime];
// This could also allows for indefinite running with:
[packetCapture capturePacketsUntilDate:[NSDate distantFuture]];
If PacketCapture requires an instance of IPGeo to function, then PacketCapture should accept IPGeo as part of its initialiser (again, I renamed the method to what I think it should be):
PacketCapture *packetCapture = [[PacketCapture alloc] initWithIPGeo:ipGeo interface:#"en1"];
Alternatively, the PacketCapture class could allocate and initialise the IPGeo class itself, but this creates a tight couple between the two classes.
Where should I instantiate the GeoIP class?
If PacketCapture requires an IPGeo object to function, I would recommend instantiating it within PacketCapture's -init method. If you need to configure the IPGeo object (by passing a different path when initialized, for example) before using it in PacketCapture, and the path to use is determined in a way PacketCapture shouldn't be aware of, have the class responsible for managing the two (or, in your case, the main loop of the program) create an instance and pass it as an argument to a custom initializer method on the PacketCapture class.
A bit of nitpicking--your initializer for IPGeo doesn't follow Objective-C conventions for expressive method names. -initWithPathToDatabase: would look a lot better in my opinion.
In a similar vein, you can add a -initWithIPGeo: method to your PacketCapture class.
Is having an infinite loop inside an object in a class bad OO design?
If this is an iOS or Cocoa application, then definitely yes. If it's a command line tool, then maybe. Furthermore, if that infinite loop is being run on the main thread, it will block all UI elements, which will make your program look unresponsive.
A better alternative would be to use the protocol/delegate pattern, a staple of Objective-C programs. Create a protocol with something like the following method:
- (void)packetCapture:(PacketCapture *)packetCapture didReceiveDataFromIPAddress:(NSString *)ipAddress;
Then create a delegate object that implements this protocol and performs an action on receiving the packets. If you're making a command line tool, you can instantiate that delegate and wait to receive messages.
By "GeoIP class", do you mean IPGeo? Where you instantiate it depends on several factors. If you only need it inside one parsing method in the IPGeo class, instantiate it there. If you need it throughout the lifetime of an IPGeo object, instantiate it in the designated initialiser instead and keep it in an instance variable.
An infinite loop is not intrinsically a bad thing, but there are usually better approaches. What platform are you developing for?
By the way, NSString literals have a # prefix, such as: #"foo". You are using plain C strings, which will probably cause a crash if you pass them into any Objective-C code that is expecting an NSString.
I am trying to subclass NSArray, but it crashes the app when trying to access the count method. I know that NSArray is a class cluster.
But what does this mean?
Is there a work around to be able to subclass an NSArray?
I know that I can simply subclass NSObject and have my array as an instance variable but I would rather subclass NSArray.
EDIT:
Reason:
I am creating a card game, I have a class Deck which should subclass NSMutableArray to have a couple of extra methods (-shuffle, -removeObjects:, -renew, etc), and I think it will look cleaner to subclass NSArray rather than having a var.
The problem with adding a category on a class like this is that all instances of the class will inherit the additional methods. That's both unnecessary (since not every array needs to be able to be shuffled, etc.) and dangerous (because you can't benefit from typechecking to be sure the NSArray you're currently referring to is really one that was expected to be shuffled).
An alternative would be to create your own Deck class that has an NSMutableArray as an instance variable. There you can define actions on your deck exactly as you would like, and the fact that you are using an NSMutableArray becomes an implementation detail. This lets you take advantage of typechecking at compile-time and it lets you change the internal implementation of your Deck class without changing its clients. For instance, if you decided for some reason that an NSMutableDictionary would be a better backing store, you can make all those changes within the implementation of your Deck class without changing any of the code that creates and uses the Deck.
You usually won't need to subclass it, but in any case the suggestions made by Apple are:
Any subclass of NSArray must override the primitive instance methods count and objectAtIndex:. These methods must operate on the backing store that you provide for the elements of the collection. For this backing store you can use a static array, a standard NSArray object, or some other data type or mechanism. You may also choose to override, partially or fully, any other NSArray method for which you want to provide an alternative implementation.
Did you actually override countmethod? As they say you have to provide your own backing structure to hold array elements, and override suggested methods considering this..
If you're just adding new methods, and using the existing backing store, then a better approach is to add a category to NSArray. Categories are a really powerful part of objective-C - see cocoadev for some samples.
NSMutableArray already has a - (void)removeObjectsInArray:(NSArray *)otherArray;
You're going to be best off making an NSObject subclass with a mutable array property.
In this particular case, I'd shuffle the array using -sortedArrayUsingComparator: and make your comparator randomly return NSOrderedAscending or NSOrderedDescending.
E.G:
NSArray *originalArray; // wherever you might get this.
NSArray *shuffledArray = [orginalArray sortedArrayUsingComparator:
^(id obj1, id obj2) {
return random() % 2 ? NSOrderedAscending : NSOrderedDescending;
}];
In my code, I have something that looks like this:
#implementation MyClass
- (id) initWithType:(NSInteger)type {
[self release];
if (type == 0) {
self = [[MyClassSubclass1 alloc] init];
} else {
self = [[MyClassSubclass2 alloc] init];
}
return self;
}
//...
#end
which I think handles any potential memory leaks. However, I have seen code out there that does something similar, except it doesn't release self before reassigning it to another newly allocated instance. Is it not necessary to release self here or is the other code I've seen incorrect?
Your code looks technically correct, from a memory management perspective. Replacing self with a different alloc'd object loses the pointer to the original object, and nobody else will be able to release it, which would cause a leak. Try commenting out the release call and run it with Leaks in Instruments.
Just be cautious about opening this particular can of worms — Foundation.framework (part of Cocoa) uses class clusters for collections and strings, but doing so is a fairly advanced concept. A better approach might be to have a class method for each subclass, using the AbstractFactory pattern.
In any case, determining the subclass type based on an integer is a bad idea — any change in mapping from type to class will break dependent code. If you're going that way, why not just pass in the class object itself?
This looks like poor use of object-oriented design.
If you're creating a different instance depending on a type variable, then why don't you have subclasses for those types?
It would be much cleaner to define a base class with all the common functionality, and a subclass for each "type" variation.
What does the class do? We might be able to point you in the right direction.
Code-wise, your example code is correct, but it's generally bad practice to replace the instance with a different instance. Unless the init method is a factory method re-using instances or a singleton initializer, avoid releasing self en-lieu of another instance.
I'm creating a base class that has an isDirty flag. It is set any time one of its properties changes, but since it's a base class, it doesn't know what its properties are. So basically, on every subclass, I have to override every - set: method to something like this:
- (id) setName:(NSString *)value {
if ([name isEqualToString:value]) {
return;
}
[name autorelease];
name = [value retain];
isDirty = YES; //Here's the important bit
}
Almost every line of that is what the automatically-synthesized setter would do. Is there any way I can override what #synthesize actually creates?
There are other options I have come up with, but they all seem like they would be much slower at runtime than this method. I've thought of things like adding an object to observe its own property changes, or creating a generic function to do all that and just pass in the address to the iVar and the new value, but that still requires overriding the setter.
Any ideas? If it makes a difference, it's for an iPhone app.
Several issues here:
(1) If you are concerned about setter performance, you shouldn't be using -isEqualToString: in your setter. Do a pointer compare instead because that is all that matters in this context.
(2) If you have an NSString attribute, you should be copying on set. Copy is free for immutable strings and will save your bacon for mutable strings (by preventing the caller from mutating the string out from under you).
(3) Again with performance; you checked for equality, but then use autorelease. That incurs unnecessary overhead.
(4) * they all seem like they would be much slower at runtime* indicates that you haven't actually tried it, haven't identified a performance problem, and are prematurely optimizing your code. Given (1) and (3), there is likely much more easily addressed performance issues.
My suggestions:
(1) Use #synthesize. It will generate correct and fast code, addressing (1) and (3).
(2) Use KVO or one of the other mechanisms. Until you identify a performance problem through instrumentation and quantification, you don't have a performance problem.
(3) Consider using CoreData (unless, of course, you are targeting OS 2.x). The example code is from something that is obviously a model object. If your code is nicely factored into model/view/controller, using CoreData at the model layer can both simplify your application and CoreData does a wonderful job of change tracking.
There's no way I know of that enables you to override what #synthesize does.
At the end of the day, it's used for creating basic accessor methods - ie. those that don't have specific behaviour.
Maybe you should look into Key Value Coding and Key Value Observing?
There isn't.
What you want to achieve is only possible by digging deep into the Objective-C runtime or by using proxy objects.
Why don't you have a look at KVO again?
If you write your own accessor method(s) #synthesize respects that. #synthesize gives precedence to accessors you write on your own. Just provide the accessor you like and #synthesize will be ignored on that one. For example you could implement an accessor that creates the property only in case it isn't already there.
Example:
#synthesize standardUserDefaults;
- (NSUserDefaults *)standardUserDefaults {
NSLog(#"standardUserDefaults");
if (!standardUserDefaults) {
NSLog(#"standardUserDefaults new");
self.standardUserDefaults = [NSUserDefaults standardUserDefaults];
}
return standardUserDefaults;
}
Here the "setter" is synthesized while the "getter" is not.
I have an object called Settings that inherits from NSMutableDictionary. When I try to initialize this object using
Settings *settings = [[Settings alloc] initWithContentsOfFile: #"someFile"]
it returns an object of type NSCFDictionary. As a result, it doesn't recognize my additional methods. For example, when I call the selector "save", it objects:
[NSCFDictionary save]: unrecognized selector sent to instance 0x524bc0
Of course, it's OK when I initialize using the garden variety
Settings *settings = [[Settings alloc] init]
I tried to cast it again to Settings but that didn't work. This seems really simple - what am I missing?
Thanks
NSDictionary is a class cluster. This means that the value returned from its init methods is not strictly an NSDictionary, but a subclass that implements the actual functionality. In almost every case, it is better to give your class an NSDictionary as an instance variable or to simply define a category on NSDictionary.
Chuck is correct about NSDictionary (and Dave, by extension, about NSArray/Set/String) and class clusters. Odds are that -[NSDictionary initWithContentsOfFile:] calls down to a different initializer than -init does, which is why it swaps out your allocated Settings instance for another subclass of NSMutableDictionary. (The initialization action when reading from a file may select a particular known subclass of NSDictionary which performs well for loading from a file, etc.)
I'll echo Chuck's guidance that it is almost always better to use composition or categories than inheritance for an NSDictionary. It's highly likely that you could accomplish what you're doing with categories in a much simpler way, and expose yourself to fewer potential bugs in the process. Consider yourself warned before deciding to subclass.
That being said, both NSDictionary and NSMutableDictionary have been designed to support subclassing, and on rare occasions that's the right thing to do. Think long and hard about it before trying it. If you find it's the right choice for your design, here are some key points to know and do as needed:
Override the following primitive methods from NSDictionary:
-count
-objectForKey:
-keyEnumerator
-initWithObjects:forKeys:count: (designated initializer)
Override the following primitive methods from NSMutableDictionary:
-setObject:forKey:
-removeObjectForKey:
If you're supporting NSCoding, be aware of classForKeyedArchiver and replacementObjectForKeyedArchiver: (both instance methods from NSObject) — they can totally change how your class responds, and you often unintentionally inherit some odd behavior from NS(Mutable)Dictionary. (You can verify if they are the culprit by setting a breakpoint on them, or implementing them to call super and breaking on your own code.)
I've implemented a number of these points in an NSMutableDictionary subclass of my own. You can check it out and use the code however may be helpful to you. One that particularly helped me (and could be the solution for your problem) was overloading the designated initializer, which is currently undocumented (Radar #7046209).
The thing to remember is that even though these bullets cover most common uses, there are always edge cases and less common functionality to account for. For example, -isEqual: and -hash for testing equality, etc.
If you actually read the spec for NSDictionary (a rash action, I know) you'll find a section named "Subclassing Notes". In it you will read:
If you do need to subclass NSDictionary, you need to take into account
that is represented by a Class cluster—there are therefore several
primitive methods upon which the methods are conceptually based:
initWithObjects:forKeys:
count
objectForKey:
keyEnumerator
In a subclass, you must override all these methods.
From https://stackoverflow.com/a/1191351/467588, this is what I did to make a subclass of NSDictionary works. I just declare an NSDictionary as an instance variable of my class and add some more required methods. I don't know what to call them though.
I posted my code sample here https://stackoverflow.com/a/10993594/467588.
This question is very old, and since most of these answers were posted, Apple has introduced object subscripting, which allows you to make your own classes behave more like NSMutableArray or NSMutableDictionary. This is simpler than the alternatives discussed above.
At a minimum, you have to override these methods:
//Array-style
- (id)objectAtIndexedSubscript:(NSUInteger)idx;
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx;
//Dictionary-style
- (id)objectForKeyedSubscript:(id <NSCopying>)key;
- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;
Here's a nice tutorial on how to do just that.