I have a property self.shareURL that may or may not be nil and I'd like to wrap it in array. Obviously, if it's nil I can't do that, so I'd like to have an empty array in that case. So I can write:
NSArray *items = [self shareURL] ? #[[self shareURL]] : #[];
However, I can construct it in one call to shareURL, like this:
NSArray *items = [NSArray arrayWithObjects:[self shareURL], nil];
This works because arrayWithObjects: will stop anyway once it sees the first nil and the stack is not corrupted because Objective-C ABI doesn't require it to clear the varargs in the stack.
Is it ok to use the second form? Or is the first one more clear?
Both options seem ok, but if you are asking for which one is more clear, I'd go for this one:
NSArray *items = [self shareURL] ? #[[self shareURL]] : #[];
Why? Because you are implementing the behaviour you want to achieve in that line, not as a consequence of something happening on the stack like on the second approach. In other words, you are achieving the behaviour you are specifying in the line.
If I'm a developer and I see that code, with the 1st approach I'll understand the behaviour, without any explanation.
Rather than terseness, I would opt for readability:
NSArray *items = nil;
if ([self shareURL]) {
items = #[[self shareURL]];
} else {
items = #[];
}
Both ways are OK, and the first one you can write like this:
NSArray *items = [self shareURL] ? : #[];
This is right.
NSArray *items = [self shareURL] ? #[[self shareURL]] : #[];
Ok, for the record, I actually went with
NSMutableArray *items = [NSMutableArray new];
if (self.shareURL) {
[items addObject: self.shareURL];
}
as it makes more clear that there is a "default" state of empty array, and we try to add one object.
Related
In the code below I thought the second condition would be true, but it is turning out as false. Am I missing something? Please help me understand.
NSArray *array = [[NSArray alloc] init];
NSLog(#"%#", NSStringFromClass([array class]));
if ([array isMemberOfClass:[NSObject class]]) {
NSLog(#"Member NSObject"); //Didn't print;
}
if ([array isMemberOfClass:[NSArray class]]) {
NSLog(#"Member NSArray"); //Didn't print; I don't understand why?
}
if ([array isKindOfClass:[NSObject class]]) {
NSLog(#"Kind of NSObject"); //Printed; Expected
}
if ([array isKindOfClass:[NSArray class]]) {
NSLog(#"Kind of NSArray"); //Printed; Expected
}
Edit
I created sub class of NSArray as MyArray and tested its instance using isMemberOfClass as below
if ([myArray isMemberOfClass:[MyArray class]]) {
NSLog(#"Member MyArray"); //Printed;
}
So, I guess isMemberOfClass not possible on NSArray, probably on some other framework classes as well.
Thanks.
This is the correct behavior. Try inspecting the actual class for that object:
NSArray *array = [[NSArray alloc] init];
NSLog(#"%#", NSStringFromClass([array class]));
The output you get is something like:
2013-02-15 23:42:31.272 Project[91998:c07] __NSArrayI
So the actual class is __NSArrayI (a private subclass of NSArray), not NSArray itself. Typically, isKindOfClass: provides more useful results.
NSArray is a class cluster. When you create an object of NSArray, internally it creates the object from its cluster. It adds simplicity to avoid creation of different type of objects depending upon the requirements.
For such cases you should use the function isKindOfClass. It checks the completer hierarchy to identify the kind of object.
You should be using isKindOfClass. Refer this for the difference.
I'm pretty sure this is eactly the same problem as in componentsJoinedByString gives me EXC_BAD_ACCESS
basically, an array is populated using this code, with ARC turned on:
-(NSMutableArray *)getArrayOfCommaSeparatedSelectorStrings{
NSMutableArray *Array = [[NSMutableArray alloc] init];
for(NSMutableArray *e in [self getArrayOfSelectorArrays]) {
[Array addObject:[displayCSSInformation returnArrayAsCommaList:e]];
}
return Array;
}
and then displayCSSInformation tries to return a comma separated list with this method :
+(NSString *)returnArrayAsCommaList:(NSMutableArray *)ToBeConverted{
NSString *test = [ToBeConverted componentsJoinedByString:#", "];
return test;
}
Thanks for your help.
There's usually no need to use a separate method if all that method does is invoke another method. Remove your +returnArrayAsCommaList: method and just use componentsJoinedByString: on the array directly.
- (NSMutableArray *) getArrayOfCommaSeparatedSelectorStrings
{
NSMutableArray *array = [[NSMutableArray alloc] init];
for (NSMutableArray *e in [self getArrayOfSelectorArrays])
[array addObject:[e componentsJoinedByString:#", "]];
return array;
}
The above should work (it works in my small test example), if you are still getting errors:
Make sure that getArrayOfSelectorArrays is actually returning an array of array of strings. Log the output to the console or step through with a debugger.
Use the “Build & Analyze” option to have the static analyser check for any issues. This is less of an issue with ARC but it will still pick up things such as using uninitialised values.
Make sure you have properly bridged ownership from any Core Foundation objects.
I'm new to Objective-C, so I may be way off...
I have this in my 'viewDidLoad' method:
NSArray *myArray;
NSString *cow = #"Cow";
NSString *pig = #"Pig";
NSString *frog = #"Frog";
NSString *sheep = #"Sheep";
myArray = [NSArray arrayWithObjects: cow, pig, frog, sheep, nil];
randomNumber.text = [myArray objectAtIndex: arc4random() % (4)];
I want to make this its own method, so I can get a random animal any time I want...but I need this to happen when the program starts. How do I access a method like this?
I may be way wrong, so I'm open to suggestions, corrections, and anything you think is helpful.
Like this:
- (void)generateAnimal{
NSArray *myArray;
NSString *cow = #"Cow";
NSString *pig = #"Pig";
NSString *frog = #"Frog";
NSString *sheep = #"Sheep";
myArray = [NSArray arrayWithObjects: cow, pig, frog, sheep, nil];
randomNumber.text = [myArray objectAtIndex: arc4random() % (4)];
}
Also:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self generateAnimal;
}
As Sagi mentioned before, in this case [self generateAnimal]; would have the wanted effect. In general Objective-C (as any other object oriented language) attaches methods to classes/instances, so you can only call them on existing instances. (Obviously there are class methods etc, but more abstractly speaking)
Objective-C wants you to enclose these calls to methods in square brackets ([ ]), as seen both in Sagi's answer and in your own example ([super viewDidLoad]). All the calls follow this pattern [target method: parameter]. Hope it makes sense, just wanted to add a bit of context to Sagi's answer.
[self generateAnimal]; //would work great :)
i am adding the arrays to an nsmutabledictionary but its giving 0 count
i allocated the dictionary in viewDidLoad method.
can any body help me out please..
thanks.
- (void)creatingDictionary{
songsDict = nil;
NSMutableArray *hrSongs = [[NSMutableArray alloc]init];
for(int i=0;i<[onAirSongs count];i++){
for(int j=i;j<[onAirSongs count];j++){
Song *a1 =(Song *)[onAirSongs objectAtIndex:i];
Song *a2 =(Song *)[onAirSongs objectAtIndex:j];
if(a1.cellId == a2.cellId)
[hrSongs addObject:a2];
else{
[songsDict setObject:hrSongs forKey:[NSString stringWithFormat:#"%d",i]];
hrSongs = nil;
i=j-1;
break;
}
}//inner for
}//out for
[hrSongs release];
NSLog(#"songsdictionary count....%d",[songsDict count]);
}
You're setting songsDict to nil. You can't add an array to a dictionary that doesn't exist.
blindJesse is correct, but there are more problems here:
You are setting songsDict to nil. In Objective-C, nil-eats-messages and, thus, you are silently not doing anything at all when you [songsDict setObject:.....].
In the middle of the loop, you hrSongs = nil;. Subsequent iterations through the loop will happily call addObject: on nil, doing nothing at all.
This whole thing seems kinda weird; what does onAirSongs contain? Cells? This seems like you haven't really preserved any kind of Model-View-Controller separation. It might work, but you are in for a severe maintenance headache down the road if you ever have to change your UI.
I'm trying to made a cocoa app that read-write to a .plist file.
I can retrieve informations from the .plist, write into, but when a key (only with strings) is empty, the app don't write to the plist.
here a sample:
-
(IBAction)saveBoot:(id)sender {
NSString *errorDesc;
NSString *bootPath = #"/myplist.plist";
NSMutableDictionary *plistBootDict =
[NSMutableDictionary dictionaryWithObjects:
[NSMutableArray arrayWithObjects:
Rescan,
RescanPrompt,
GUI,
InstantMenu,
DefaultPartition,
EHCIacquire,
nil]
forKeys:[NSMutableArray arrayWithObjects:
#"Rescan",
#"Rescan Prompt",
#"GUI",
#"Instant Menu",
#"Default Partition",
#"EHCIacquire",
nil]];
NSData *plistBootData = [NSPropertyListSerialization
dataFromPropertyList:plistBootDict
format:NSPropertyListXMLFormat_v1_0
errorDescription:&errorDesc];
if (bootPath) {
[plistBootData writeToFile:bootPath atomically:NO];
}
else {
NSLog(errorDesc);
[errorDesc release];
}
}
#end
I think i need a loop to check if each key is empty or not (and remove it if empty),
but i've tried different (objectEnumerator, objectForKey:..etc) method whitout success.
If someone can help a beginner like me,
thanks in advance.
Ronan.
The problem is probably that because nil is the terminator for variable argument lists, so if, say, RescanPrompt is nil, the object array will only contain up until that part (so you can't "remove if empty" since it won't exist in the dictionary in the first place). You should probably construct your dictionary piece by piece; something like:
NSMutableDictionary *plistBootDict = [NSMutableDictionary dictionary];
if (Rescan)
[plistBootDisc setObject:Rescan forKey:#"Rescan"];
if (GUI)
[plistBootDisc setObject:GUI forKey:#"GUI"];
// etc
(Also, there's no reason to be using NSMutableArray or NSMutableDictionary if you're never going to be mutating them later.)