Objective-C arrays, style and spirit - objective-c

Newbie question. Looking at arrays (ie: dynamically sized) this works:
NSArray *array;
array = [NSArray arrayWithObjects:
#"one", #"two", nil];
This does not:
array = [NSArray arrayWithObjects:
1, 2, nil];
Ok, I get it. This works:
array = [NSArray arrayWithObjects:
[NSNumber numberWithInt:1], [NSNumber numberWithInt:2], nil];
Its sorta less "on the fly" as C++ / Java. I see the same thing with the init examples I'm reading. For example:
// pseudo objc example
MyVar v = [MyVar init]; // blank
[v setSomething];
[v setSomethingElse];
// use v down here
In C++/Java I'd do:
MyVar v = new MyVar("foo", "bar", "baz", "quux");
And I'd know that v is ready to go by default. Is there a spirit of ObjC that I should not fight? Should I just expect to write more lines and less "one-liners"?

In Objective-C, the "init" method is just a method. Unlike Java or C++ whose constructors are different than other methods. So you can define your own init methods that behave like C++ or Java constructors. For example, you could define an init method that takes several parameters. It might look something like this.
MyVar* v = [[MyVar alloc] initWithName:#"foo" andTitle:#"bar"];
// do something with v
[v release];
Common practice is to simply create new methods that perform object initialization, and prefix the method name with "init" for clarity and consistency.

NSArray, its modifiable variant NSMutableArray, and all collection structures (NSDictionary, NSSet) are made for storing objects. This is why you see [NSNumber numberWithInt:1] instead of simply 1.
For strings, note that an Objective-c string (like #"one", including leading #) is an object of type NSString, whereas a C string (like "one", without #) is not an object.
If you want the simplicity of storing simple values in arrays, don't forget that Objective-C is a superset of C. This means that you can use a declaration like:
int array[] = { 1, 2, 3 };

What you get with Objective-C's verbosity is greater readability.
Sure, while you're writing code, it might be easy to knock up something like you wrote
MyRect rect = new MyRect(1.0, 1.0, 3.0 3.0);
But when you, or more likely someone else, comes to maintain your code then I would argue that this is much easier to read:
MyRect *rect = [[MyRect alloc] initWithX:1.0 Y:1.0 width:3.0 height:3.0];
And in these days of smart editors it isn't that much harder to write.

You can write constructors that take named parameters to get it down to one line:
MyVar *v = [[MyVar alloc] initWithFoo:#"foo" bar:#"bar" baz:#"baz" quux:#"quux"];

Yeah, Objective-C is pretty verbose, so you're going to need to get used to that.
The MyVar init case is not quite right; you generally need to do a [[MyVar alloc] init] or a [MyVar new]. And in many cases, there are init variants that take arguments, like
MyVar v = [[MyVar alloc] initWithSomething: "foo" somethingElse: "bar"];
If you want something less verbose, where you can get more one liners in, you might want to look into MacRuby, which is a binding between Ruby and Objective-C that gives you access to all of the Cocoa APIs but with a much more compact, high level syntax (and you can go even more compact still with HotCocoa. Of course, there is a bit of a performance penalty using MacRuby, so if you're doing a high performance game it might not be ideal to write your drawing loop in RubyCocoa, but its fine for the vast majority of applications.

Related

Getting the class type for a nil object?

If I have an object that is already allocated, then doing object.class returns a non-nil value. So far so good. But, if the object has not yet been allocated, then accessing object.class returns nil.
I want to allocate an object based on its type dynamically, so for example:
#property NSArray *myArray;
...
// myArray is nil so far
self.myArray = [_myArray.class new];
However, I can't do this because _myArray.class is returning nil. So how would I determine the class type of a nil instance?
Update:
It is in fact possible. Check out my answer below.
You cannot determine the class of a nil instance, because it does not have one: it can be, quite literally, of any type derived from the type of the variable. For example, NSMutableArray is perfectly compatible with NSArray:
NSArray *myArray = [NSArray new]; // OK
NSArray *myArray = [NSMutableArray new]; // Also OK
Since the run-time capabilities of different subclasses can vary a lot, it is always up to your program to decide what kind of objects it wants.
Objective-C is a duck-typed language. This means that there are several things you can or can't do, and one of the things you can't is statically get a reference to the type of a variable.
Specifically, in your expression:
[_myArray.class new]
First, _myArray.class is evaluated, and then the result is sent the new message. Since _myArray is nil to begin with, _myArray.class returns nil as well, and the new message will return nil too, because sending any message to nil returns nil (or the closest representation to zero the return type has). This is why it doesn't work.
I suspect you come from a strongly-typed language like C#; what you're doing right now is the equivalent of Foo foo = (Foo)Activator.CreateInstance(foo.GetType()), which is sure to fail because foo.GetType() will either not compile or throw an exception (depending on if it's a class field or a local variable) since it was never assigned a value. In Objective-C, it compiles but it doesn't works. What you would want is Activator.CreateInstance(typeof(Foo)), but notice that Foo is now hardcoded here too, so you might as well just create a new Foo().
You say that the compiler "knows the type" of the object. This is not exactly true. First, NSArray and NSMutableArray are the root classes of the NSArray class cluster. This means that both are abstract, and [NSArray alloc] and [NSMutableArray alloc] return an instance of a subclass (NSCFArray last time I checked, and possibly something else; I recall seeing _NSArrayM). Maybe [NSArray new] works, but it's not giving you a plain NSArray.
Second, type safety is not enforced. Consider this code:
id foo = #"foo";
NSArray* bar = foo; // no warning!
So even though the compiler thinks that bar is an NSArray, it's in fact a NSString. If we plug in your code:
id foo = #"foo";
NSArray* bar = foo; // no warning!
NSArray* baz = [bar.class new];
baz is now an NSString as well. Since you ask for the runtime class of bar, the compiler has nothing to do with the operations.
And precisely because of that kind of behavior, you should probably instantiate your object with a class that you know, using [NSArray new] instead of trusting _myArray to be non-nil, and to be what you think it is.
You must init the property , or it will be nil , send a message to a nil object , it will return nil , so ,you must first init the array like _array = [[NSArray alloc] init];
So, for anyone wondering if this is possible, it is:
objc_property_t property = class_getProperty(self.class, "myArray");
const char * const attrString = property_getAttributes(property);
const char *typeString = attrString + 1;
const char *next = NSGetSizeAndAlignment(typeString, NULL, NULL);
const char *className = typeString + 2;
next = strchr(className, '"');
size_t classNameLength = next - className;
char trimmedName[classNameLength + 1];
strncpy(trimmedName, className, classNameLength);
trimmedName[classNameLength] = '\0';
Class objectClass = objc_getClass(trimmedName);
NSLog(#"%#", objectClass);
Output:
NSArray
Done with the help of extobjc.
Nil has no class type
In Objective-C the actual class on an instance variable is only determined at runtime. So, you can't know the class of a nil object.
This is not an issue in your situation since you only need to do:
NSArray *myArray = [NSArray new];
Or
NSArray *myArray = [[NSArray alloc] init];
In Objective-C most decisions are deferred to the runtime
(as much as possible)
Objective-C is a runtime oriented language, which means that when it's
possible it defers decisions about what will actually be executed from
compile & link time to when it's actually executing on the runtime.
This gives you a lot of flexibility in that you can redirect messages
to appropriate objects as you need to or you can even intentionally
swap method implementations, etc.
This requires the use of a runtime
which can introspect objects to see what they do & don't respond to
and dispatch methods appropriately. If we contrast this to a language
like C. In C you start out with a main() method and then from there
it's pretty much a top down design of following your logic and
executing functions as you've written your code. A C struct can't
forward requests to perform a function onto other targets.
Source: Understanding the Objective-C Runtime

Why are these two NSString pointers the same?

When I alloc and init two NSString variables and compare their pointers, they are the same. Here's a snippet that shows this:
NSString *s1 = [[NSString alloc] initWithString:#"hello world"];
NSString *s2 = [[NSString alloc] initWithString:#"hello world"];
if (s1 == s2) {
NSLog(#"==");
}else {
NSLog(#"!=");
}
Why are s1 and s2 the same?
There are three things going on here:
Firstly, the two identical string literals you're passing in to initWithString: will have the same address to start. This is an obvious optimization for constant data.
Secondly, when you nest alloc and init with strings, the runtime performs an optimization, the alloc call essentially becomes a no-op. This is done using the NSPlaceholderString class. This means the pointer you get back here will be coming from initWithString:, not from alloc.
Thirdly, under the hood, initWithString: is calling CFStringCreateCopy, which as you may find, has the following behavior: Since this routine is for creating immutable strings, it has an optimization. It simply calls CFRetain() and returns the same object that was passed in.
Thanks for the very interesting question. I had fun figuring it out.
#"hello world" strings are of class NSConstantString.if you use #"hello world" in two places, they will be referencing the very same object.
From documentation.
The simplest way to create a string object in source code is to use
the Objective-C #"..." construct:
NSString *temp = #"/tmp/scratch"; Note that, when creating a string
constant in this fashion, you should use UTF-8 characters. Such an
object is created at compile time and exists throughout your program’s
execution. The compiler makes such object constants unique on a
per-module basis, and they’re never deallocated, though you can retain
and release them as you do any other object. You can also send
messages directly to a string constant as you do any other string:
BOOL same = [#"comparison" isEqualToString:myString];

How to fill NSArray in compile time?

In Objective-C, how to do something like is
int array[] = {1, 2, 3, 4};
in pure C?
I need to fill NSArray with NSStrings with the smallest overhead (code and/or runtime) as possible.
It's not possible to create an array like you're doing at compile time. That's because it's not a "compile time constant." Instead, you can do something like:
static NSArray *tArray = nil;
-(void)viewDidLoad {
[super viewDidLoad];
tArray = [NSArray arrayWithObjects:#"A", #"B", #"C", nil];
}
If it's truly important that you have this precompiled, then I guess you could create a test project, create the array (or whatever object) you need, fill it, then serialize it using NSKeyedArchiver (which will save it to a file), and then include that file in your app. You will then need to use NSKeyedUnarchiver to unarchive the object for use. I'm not sure what the performance difference is between these two approaches. One advantage to this method is that you don't have a big block of code if you need to initialize an array that includes a lot of objects.
use this
NSArray *array = [NSArray arrayWithObjects:str1,str2, nil];
As far as i understand you need a one-dimentional array
You can use class methods of NSArray.. For instance
NSString *yourString;
NSArray *yourArray = [[NSArray alloc] initWithObjects:yourString, nil];
If you need more, please give some more detail about your issue
Simple as that: NSArray<NSString*> *stringsArray = #[#"Str1", #"Str2", #"Str3", ...]; Modern ObjectiveC allows generics and literal arrays.
If you want shorter code, then NSArray *stringsArray = #[#"Str1", #"Str2", #"Str3", ...];, as the generics are optional and help only when accessing the array elements, thus you can later in the code cast back to the templatized array.

Objective-c Workaround for running intepretted code

I'm used to using eval() in languages like javascript e.t.c.
In cocos2d, to select the level, I am passing "1" or "2" to my Loadlevel.m file, the level Classes are named "LevelOne" and "LevelTwo" accordingly, I wanted to create a dictionary lookup that paried "1" => "LevelOne" e.t.c then run eval on that string to effectively call [MyLevel node];
Apparently we can't use eval in IOS code, so how would I go about doing this?
Try using the NSStringFromClass and NSClassFromString functions.
Specifically, represent the classes as strings in your dictionary, e.g.,
[myDictionary setObject:NSStringFromClass([LevelOne class]) forKey:#"1"]
Then, to use the right level from your dictionary, you could do:
NSString *levelAsString = [myDictionary objectForKey:#"1"];
id node = [NSClassFromString(levelAsString) node]
(I'm assuming +node is a class method)
Now, this is a pretty uncommon and odd design in Cocoa. Perhaps if you explain more about what you're doing, we could suggest alternate design choices that might be better.
I'm not one hundred percent clear on what you're asking, but you can store class objects directly in your dictionary, retrieve them, and send them messages:
NSDictionary * d = [NSDictionary dictionaryWithObjectsAndKeys:[LevelOne class], #"1", [LevelTwo class], #"2", nil];
Class selectedLevel = [d objectForKey:#"1"];
[selectedLevel node];

Using accessor methods to set iVars?

Initially, I was looking at the way "pickerData" is set and thinking I wonder why you can't just assign it directly (as in METHOD_002), but then I stated thinking that I should really be using the accessor methods I defined and not setting the instance variables directly. Am I understand this correctly that METHOD_001 is a better way of doing this?
#property(nonatomic, retain) IBOutlet NSArray *pickerData;
METHOD_001
-(void)viewDidLoad {
NSLog(#"VIEW: Single ... Loaded");
NSArray *dataArray = [[NSArray alloc] initWithObjects:#"A", #"B", #"C",nil];
[self setPickerData:dataArray];
[dataArray release];
[super viewDidLoad];
}
-(void)dealloc {
[pickerData release];
[super dealloc];
}
OR METHOD_002
-(void)viewDidLoad {
NSLog(#"VIEW: Single ... Loaded");
if(pickerData != nil) [pickerData release];
pickerData = [[[NSArray alloc] initWithObjects:#"A", #"B", #"C", nil] retain];
[super viewDidLoad];
}
-(void)dealloc {
[pickerData release];
[super dealloc];
}
EDIT_001:
First off I have added the "nil" values to terminate the NSArrays, coming from C I always forget this, my bad. Also you're right, I did not account for the fact in METHOD_002 that pickerData might already be set and as a result leak the old object. Once you start noticing these problems and fixing the code it starts to look like METHOD_001 is the best idea. Or to just use the property directly as Vladimir and eJames noted.
self.pickerData = [NSArray arrayWithObjects: #"A", #"B", #"C", nil];
EDIT_002:
Thank you for all the pointers and comments, for now I am going to stick with METHOD_001, I could just as easily use NSArrayWithObjects: but I am trying to keep memory usage low by releasing things myself as soon as I can (not that it matters here, but for future projects) Also I do like the feel of self.pickerData, but I am still unsure how I feel about dot-notation and have for now been trying to stick with old style objects and messages where possible. Again many thanks for the help.
gary
You should always use accessors of properties (which in Objective-C 2.0 means using the self.property notation.)
Why? Because it provides automatic access control and object life-cycle management. The generated accessors can provide a lot protection, such as read/write, copy, retain etc that take a lot of manual code otherwise. If you write your own accessors, you can add in all the validation and side effects you want.
(Back before Objective-C 2.0 writing accessors was considered a high art. It still can be if you fully exploit the potential.)
The only time you should access properties directly is when you are writing an accessor. For example take this common pattern:
#property(nonatomic, retain) NSMutableArray *myObjects;
#synthesize myObjects;
-(NSMutableArray *) myObjects{
if (myObect!=nil) {
return myObect;
}
NSMutableArray *anArray=[[NSMutableArray alloc] initWithCapacity:1];
self.myObject=anArray;
[anArray release]
return myObject;
}
This accessor ensures that myObjects is never nil which removes a lot of boilerplate nil testing in the rest of your code.
You obviously can't call self.myObjects (which is really [self myObjects] )inside the accessor without creating an infinite recursion so you must access the raw variable here but...
...You can call (the autogenerated) self.myObjects= (which is really [self setMyObjects:anArray] ) because it's an entirely different method. If you look at the internals of setMyObjects: you would see that it access the raw variable as well.
If you use the generated accessors, self.myObjects= handles retaining, copying, nilling etc for you, every time you call it. The only time you have to call release is in the dealloc. This alone wipes out probably half the errors people make in Objective-C.
Conversely, outside of an accessor method, you gain absolutely nothing by directly accessing the properties inside the classes own methods. All it does is save some key strokes while exposing you to the risk of hard to find bugs.
As the previous answers demonstrated, you made several memory errors by trying to manage the property directly. Had you used the accessor every time, you would not have made them. For example:
pickerData = [[[NSArray alloc] initWithObjects:#"A", #"B", #"C", nil] retain];
... has to be managed exactly right every time whereas ...
self.pickerData = [[NSArray alloc] initWithObjects:#"A", #"B", #"C", nil];
... is automatically correct.
Remember that the ultimate design goal for any Objective-C class is that is should be perfectly modular and reusable. That means it should manage all its own memory, its own data validation and its own side effects. Accessors are absolutely vital to that management. By wrapping logic around every access of a variable, you ensure that (1) it is the type, range, etc you expect and (2) that it is always around when you need it be (3) that you can control all the side effects of writing or reading the variable and (4) that it doesn't leak.
I cannot extol the virtues of accessors enough. In fact, I might write a little song. ;-)
There are a lot of topics to cover here, but I'll start with the recommended approach:
METHOD_003
-(void)viewDidLoad {
NSLog(#"VIEW: Single ... Loaded");
self.pickerData = [NSArray arrayWithObjects:#"A", #"B", #"C", nil];
[super viewDidLoad];
}
Note: thanks to Vladimir for the reminder to nil-terminate.
Now, for the details:
METHOD_001 does the exact same thing as METHOD_003, but it takes a few more lines of code.
METHOD_002 has a memory leak because you alloc the array, and then also retain it. This results a total retain count of 2. When you release the array in your dealloc method, the count will be reduced to 1, so the array will not be released from memory.
Even if you remove the extra retain from METHOD_002, it will not do two very important things:
It will not send the proper KVC/KVO notifications. Cocoa does a lot of very convenient things behind-the-scenes when you use a property accessor, so unless you have a good reason not to, using the accessors is highly recommended.
It will not automatically release any old data that was stored in pickerData. This is not a concern in viewDidLoad, but if you were in a different method, it would make a difference, so it is best to just get in the habit of using the accessors unless you have a specific reason not to.
The biggest problems with your METHOD_002 are: a) you're retaining an array you already own, which you don't need to do (and will leak the array), and b) you're not accounting for the possibility that pickerData may already have a non-nil value (viewDidLoad may be called many times over the life of a controller).
As a matter of style, the first method is probably better. It would have avoided (b), and generally handles a lot of niggling details so you don't have to.