Can I use NSPredicate for this? - objective-c

Here's my loop:
- (NSArray *)myArray
{
if (!_myArray)
{
NSMutableArray *array = [NSMutableArray array];
for (MyReport *report in self.helper.myReportType.reports)
{
[array addObject:report.nameString];
}
_myArray = array;
}
return _myArray;
}
This works (with obviously some casting happening, which may not be great or desirable), but surely there's a better way to do this. Can NSPredicate help here? (I'm still new to using NSPredicate, but I believe it's primarily for filtering data, not building an array like this?) Otherwise, how can I rewrite this using another Apple helper class?

NSPredicate is more about filtering data, like you said. A clean way to do this is with Key-Value Coding, which when used on NSArray, calls the valueForKey: method on each of its objects, and returns the results as an NSArray:
_myArray = [self.helper.myReportType.reports valueForKey:#"nameString"];
Note that this method converts nil to NSNull automatically. More advanced KVC-Collection operator information can be found here: http://nshipster.com/kvc-collection-operators/

Use below code-
[self.helper.myReportType.reports valueForKey:#"nameString"];
It will return you array of nameString's from reports array.

Related

NSArray create with some elements plus another NSArray

I am trying to use model inheritance on realm. So I minded up using the code below to override and also call super method.
+ (NSArray *)requiredProperties {
return #[[super requiredProperties], #"thisIsRequired",#"thisIsAlsoRequired"];
}
So the question: is it OK to create an NSArray on the fly while also using another NSArray and some more elements:
NSArray *mySecondArray = #[myFirstArray, #"andSomeExtras", #"alsoMoreExtras"];
What I have been expecting is; first element of mySecondArray should be the first element of myFirstArray. Second element of mySecondArray should be the second element of myFirstArray and so on. (size of myFirstArray) +1 th element of mySecondArray should be #"thisIsRequired" .
Or I am making up some kind of magix?
Well, as you can see I am new to the stuff and I might be confused.
In general, it is okay to instantiate such heterogeneous arrays with Foundation. It's just not what you want here. In your example, you would end up with the following instead:
NSArray *myFirstArray = #[#"firstsFirstsElement", #"firstsSecondElement"];
NSArray *mySecondArray = #[myFirstArray, #"andSomeExtras", #"alsoMoreExtras"];
/* =>
#[
#[#"firstsFirstsElement", #"firstsSecondElement"],
#"andSomeExtras",
#"alsoMoreExtras",
]
*/
You're looking for - arrayByAddingObjectsFromArray:. You can use it like seen below:
+ (NSArray *)requiredProperties {
return [super.requiredProperties arrayByAddingObjectsFromArray:#[
#"thisIsRequired",
#"thisIsAlsoRequired",
]];
}
Yes it is Ok, as long as you will remember what you are doing anywhere in your code.
However you usually reserve the use of NSArray for a homogeneous collection of objects and use NSDictionary, or better your own DTO class, for heterogeneous aggregation of data.
NSArray official documentation show a similar example in the section dedicated to the method arrayWithObjects:
NSArray is really an array of id, even with generics enabled.
This other link about Objective C generics may also help understand the nature of the raw NSArray class.
Why not use NSMutableArray and just add the objects?
NSMutableArray *mySecondArray = [myFirstArray mutableCopy];
[mySecondArray addObject:#"andSomeExtras"];
[mySecondArray addObject:#"alsoMoreExtras"];

How to check assignment since addObject doesn't access setter?

I just noticed that calling addObject: on an NSMutableArray doesn't access that array's setter.
E.g., for NSMutableArray self.myArray, [self.myArray addObject:object] does not use [self setMyArray:array] to add the object.
Previously I have been using custom setters and getter to check assignment before assigning; e.g., if I wanted an array that only accepted objects of class MyClass, I would do the following:
- (void)setMyArray:(NSMutableArray *)myArray
{
for (id object in myArray)
{
if (![object isKindOfClass:[MyClass class]]) return;
}
_myArray = myArray;
}
- (NSMutableArray *)myArray
{
if (!_myArray) _myArray = [[NSMutableArray alloc] init];
_myArray = myArray;
}
How do I go about achieving this same functionality when changing the array via addObject:, removeObject:, and other similar functions that may circumvent the setter?
Generally this kind of problem is the reason why NSMutableArray is usually avoided in preference of NSArray.
This is the simple solution, use NSArray instead of NSMutableArray:
self.myArray = [self.myArray arrayByAddingObject:foo];
However, if the array is really big that will cause performance issues. Then you've got two options:
you can have your own addObjectToMyArray: method in your class and always use that
you can create an NSArrayController and use that to access your array. It will implement key value observing and bindings and all of that stuff.
NSMutableArray is designed to perform addObject: with as few CPU instructions as possible and therefore does not proved any way for external code to be notified that the object was added. You have to have some other class wrapped around it.
Do not try to subclass NSMutableArray, because it is a "class cluster" making subclasses extremely complicated.
If what you wish to do is ensure objects in the array are of a particular class then this answer to the question "NSMutableArray - force the array to hold specific object type only" provides code to do exactly that.
If you wish to do other checks on assignment then you can use the code in that answer as a starting point.

Add object to array only if the object is not in already

I want to add object to array only if the array already does not contain that object.
How to do opposite of containsObject method in NSArray ?
Use an NSMutableOrderedSet, whose addObject: method does exactly what you want:
Appends a given object to the mutable ordered set, if it is not already a member.
Here's how I'd do it:
if (![myArray containsObject:objectToAdd]){
[myArray addObject:objectToAdd];
}
More detail here:
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html
Note that because the containsObject method queries every object in the array there are some performance considerations when using it on larger arrays.
if your object is of NSString* type you can do fast enumeration like this
BOOL found = NO;
for(NSString *object in YourArray)
{
if([object isEqualtoString:#"My text"])
{
found = YES;
}
}
if(!found)
{
//addObject
}
there are many isEqual methods in objective-c for different data types

Static Variable in Cocoa Class Category

I have a cocoa 'category' for adding inflections (pluralize, singularize, etc.) to NSString. The code requires loading a set of regular expression rules and exceptions from a PLIST into dictionaries and arrays, as well as adding manual exceptions from code. I need a way to persist these data structures (as class members) between multiple calls to the inflection code (all instance methods). I attempted:
+ (NSMutableArray *)uncountables
{
static NSMutableArray *uncountables = nil;
if (uncountables == nil) uncountables = [NSMutableArray array];
return uncountables;
}
However, it appears to fail occasionally. Does a good way of doing this exist? I don't want to subclass NSString if possible. Thanks.
[NSMutableArray array];
returns an autoreleased array. Use this instead:
[[NSMutableArray alloc] init];
I think this code is OK. I use the same thing a lot for singletons. But be aware that it is not thread safe this way. Maybe you calling it from different threads?
As drawnonward already mentioned, [NSMutableArray array]; returns an autoreleased array. But I don't think, it's a good idea to return non-autoreleased array, because it contradicts with Cocoa memory management conceptions - only alloc, copy and new should be released manually. All other initializations are autoreleased.
So, you should just use
interface:
NSArray *a;
...somewhere in a code...
a = [[NSString uncountables] retain];
...
- (void)dealloc {
[a release];
}
to get properly retained/released objects.

Change the values within NSArray by dereferencing?

I've come across a problem related to pointers within arrays in objective-c.
What I'm trying to do is take the pointers within an NSArray, pass them to a method, and then assign the returned value back to the original pointer(the pointer which belongs to the array).
Based on what I know from C and C++, by dereferencing the pointers within the array, I should be able to change the values they point to... Here is the code I'm using, but it is not working (the value phone points to never changes based on the NSLog output).
NSArray *phoneNumbers = [phoneEmailDict objectForKey:#"phone"];
for (NSString* phone in phoneNumbers) {
(*phone) = (*[self removeNonNumbers:phone]);
NSLog(#"phone:%#", phone);
}
And here is the method signature I am passing the NSString* to:
- (NSString*) removeNonNumbers: (NSString*) string;
As you can see, I am iterating through each NSString* within phoneNumbers with the variable phone. I pass the phone to removeNonNumbers:, which returns the modified NSString*. I Then dereference the pointer returned from removeNonNumber and assign the value to phone.
As you can tell, I probably do not understand Objective-C objects that well. I'm pretty sure this would work in C++ or C, but I can't see why it doesn't work here! Thanks in advance for your help!
Yeah, that's not going to work. You'll need an NSMutableArray:
NSMutableArray * phoneNumbers = [[phoneEmailDict objectForKey:#"phone"] mutableCopy];
for (NSUInteger i = 0; i < [phoneNumber count]; ++i) {
NSString * phone = [phoneNumbers objectAtIndex:i];
phone = [self removeNonNumbers:phone];
[phoneNumbers replaceObjectAtIndex:i withObject:phone];
}
[phoneEmailDict setObject:phoneNumbers forKey:#"phone"];
[phoneNumbers release];
You can't dereference Objective-C object variables. They are always pointers, but you should treat them as though they're atomic values. You need to mutate the array itself to contain the new objects you're generating.
NSArray is not a C/C++ style array. It's an Objective-C object. You need to use the instance methods of the NSArray class to perform operations on it.
In Objective-C you never "dereference" an object pointer to set its value.
Also, you're using what is called Fast Enumeration, which does not allow mutation.
You can also use enumerateObjectsUsingBlock:.
NSArray *array = [NSArray array];
__block NSMutableArray *mutableCopyArray = [array mutableCopy];
[mutableCopyArray enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
[mutableCopyArray replaceObjectAtIndex:idx withObject:[object modifiedObject]];
}];
Checkout How do I iterate over an NSArray?
While this may work to some degree, I haven't tested it, I'd file this under 'bad idea' and not touch. NSArray, and many other cocoa objects, a fairly complex and can have a variety of implementations under the hood as part of the class cluster design pattern.
So when it comes down to it you really won't know what you're dealing internally. NSArray is actually designed to be immutable so in place editing is even doubly a bad idea.
Objects that are designed to let you mess around with the internals expose those through api methods like NSMutableData's mutableBytes.
You're better off constructing a new NS(Mutable)Array with the processed values.