Why should I use KVC rather than the simple dot syntax when accessing object properties? - iphone

There's the option to go the long way, if an receiver class conforms to the NSKeyValueProtocol:
[myInstance setValue:[NSNumber numberWithInt:2] forKey:#"integerProperty"];
or the short way:
myInstance.integerProperty = 2;
what's the point of this KVC method? When is this useful?

First, those aren't the same, the second should be:
myInstance.integerProperty = [NSNumber numbwerWithInt:2];
if integerProperty is an NSNumber.
In general you use the second form when you are doing the most things. You use setValue:forKey: and valueForKey: when you want to dynamically choose the property to store things in. For instance, think about how valueForKeyPath: against an NSArray works (for reference, if you call -valueForKey: against an NSArray it will return an array where each object is the result of asking the corresponding object in that NSArray for that value:
- (NSArray *) valueForKey:(id)key {
NSMutableArray *retval = [NSMutableArray array];
for (NSObject *object in self) {
[retval addObject:[object valueForKey:key]];
}
return retval;
}
In the above case we were able to use valueForKey: to implement our function even though we do not know what the key is beforehand, since it is passed in as an argument.

Related

Is it good practice to convert return value type from NSMutableArray to NSArray if return type of method is NSArray?

In the following code, I don't get any warning or compile error if I return NSMutableArray instead of NSArray which is the method's return type.
But is it good practice to convert return value to NSArray by using -copy method like the following code?
Or should I return NSMutableArray?
+(NSArray *)returnImutableArray {
NSMutableArray *arr = [[NSMutableArray alloc] init];
[arr addObject:#"a"];
[arr addObject:#"b"];
//return arr; // no warning
return [arr copy];
}
I think this is essentially personal preference. You incur a (probably small) performance penalty by copying the array before returning it. The upside is that you'll completely prevent mutation of the result further down the line. However, I don't usually go to the trouble. Because the declared return type of the method is an immutable NSArray, the compiler will warn you if you try to call one of NSMutableArray's mutation methods on it unless you go out of your way to prevent that (by casting to NSMutableArray).
So, in short, it's personal preference and I personally don't generally bother with the immutable copy.
This all depends on what you want to achieve with the array.
If you really don't want or need to modify the returned array, return it as an NSArray.
You could as well return it as an array, without copying it.
In both cases, don't forget the memory management.
You should never return anything with a retain count >0.
+(NSArray *)returnImutableArray {
NSMutableArray *arr = [[NSMutableArray alloc] init];
[arr addObject:#"a"];
[arr addObject:#"b"];
NSArray * returnArray = [NSArray arrayWithArray:arr];
[arr release];
//return arr; // no warning
return returnArray;
}
Or you could do so:
+(NSArray *)returnImutableArray {
NSMutableArray *arr = [[NSMutableArray alloc] init];
[arr addObject:#"a"];
[arr addObject:#"b"];
//return arr; // no warning
return [arr autorelease];
}
There's no need to copy the NSMutableArray as it extends the NSArray so your code will never break if you do so.
I return an immutable copy of interior mutable instances by default. Not just NSArray, but several other types.
I don't want mutable objects floating around, although it's not a problem that it mutates as long as you treat it as concrete.
I consider it a better default because it minimizes shallow copying if shared. That is, if you take the result and assign it to one or more objects which are copy properties, then all those actions must make concrete shallow copies, when they could effectively retain. This is the same reason you should favor copy for properties of these types, rather than retain when declaring properties.
In some cases, you just know its scope is limited and that the mutable won't get out -- in that case, I may not bother making the copy.

Sort array with custom objects

I'm trying to sort an array, filled with objects from a class i wrote. The class i wrote contains an NSString itself, as the "name". So what i wanna do, is to sort the array, based on the name property the objects contain. I tried the simple:
[anArray sortedArrayUsingSelecter: CaseInsensitiveCompare];
But as you can guess, it sends the caseInsensitiveCompare message to the objects itself, and it won't respond to that one.
So i'm guessing i have to make my objects able to respond to the caseInsensitiveCompare? Not quite sure how to write such a method, so any pointers would be lovely.
Thanks
You can use the method sortedArrayUsingComparator:
NSArray *sortedArray = [anArray sortedArrayUsingComparator:^(MyClass *a, MyClass *b) {
return [a.name caseInsensitiveCompare:b.name];
}];
You can sortedArrayUsingComparator:(NSComparator)cmptr (NSArray reference) to sort the array. For instance, if you want to sort by the name property, do the following (assuming your class is called Person):
NSArray *sortedArray = [anArray sortedArrayUsingComparator:^(id a, id b) {
NSString *first = [(Person *)a name];
NSString *second = [(Person *)b name];
return [first caseInsensitiveCompare:second];
}];

Performance Problem while retrieving custom objects from array

I create a custom object that has some properties like ID and Title,description etc...
And I add it to an array. (That array may contains more than 500 values).
And I use the following code to retrieve custom objects,
-(CustomObjects *)getObjectWithId:(int)id {
CustomObjects *objCustomObjects = nil;
for (CustomObjects *customObjects in arrayCustomObjects) {
if (customObjects.id == id) {
objCustomObjects = customObjects;
break;
}
}
return objCustomObjects;
}
But It has some performance problem, because I use the function to call on UIScrollview pinch.
How can I improve performance in fetching the objects?
thanks in advance,
A dictionary is better for this. The only catch is that you can’t have a NSDictionary with primitive int keys, so that you have to wrap the id in an NSNumber.
- (void) addCustomObject: (CustomObject*) obj {
NSNumber *wrappedID = [NSNumber numberWithInt:[obj idNumber]];
[dictionary setObject:obj forKey:wrappedID];
}
- (CustomObject*) findObjectByID: (int) idNumber {
NSNumber *wrappedID = [NSNumber numberWithInt:[obj idNumber]];
return [dictionary objectForKey:wrappedID];
}
A dictionary (also called hash table) does not have to go through all the values to find the right one, it has all the values arranged cleverly according to the keys so that it can jump to the right one or close to it. What you are doing with the array is called linear search and it’s not very efficient.
Better you can use NSDictionary with id as the key. You can easily fetch the object from the dictionary.
Is it Ok for your requirement?
You could use an NSPredicate that checks whether id equals the one you're looking for, and simply filter the custom objects using this predicate by calling filteredArrayUsingPredicate:.
To improve performance, I would try to postpone whatever you're trying to calculate by not directly calling the function that does the heavy work in your scroll view, but rather call [self performSelector:... withObject:nil afterDelay:0]; which postpones the calculation to the next runloop cycle. If you check if there's already a calculation scheduled before you call performSelector you should actually be able to reduce the frequency of the calculation while maintaining a crisp interface.
You must ditch the array in favor for a dictionary if you want to have fast lookups.
If you want to access objects both by key and index then you need to the objects around in two collections, and make sure they are in sync.
I have already done a helper class for this named CWOrderedDictionary. It's a subclass of NSMutableDictionary that allows for access to objects by both keys (as any dictionary do), and by index using methods identical to NSMutableArray.
My class is available to use for inspiration or as is from here: https://github.com/jayway/CWFoundation/
Use NSPredicate:-
You will receive the filtered array with the object that has the id you passed;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"id == %#", id];
NSArray *filtered = [arrayCustomObjects filteredArrayUsingPredicate:predicate];
Instead of intjust use [NSNumber numberWithInt:] , i did some changes in your given code.
-(CustomObjects *)getObjectWithId:(NSNumber* )id {//changed int to NSNumber
CustomObjects *objCustomObjects = nil;
NSPredicate *bPredicate = [NSPredicate predicateWithFormat:#"SELF.id==%#",id];
NSArray *result = [array filteredArrayUsingPredicate:bPredicate];
//return filtered array contains the object of your given value
if([result count]>0)
objCustomObjects = [result objectAtIndex:0];
}
return objCustomObjects;
}

How to create a NSArray of unknown NSStrings?

For example, I have #"John", #"Peter", ..., #"May" and need to construct NSArray:
[#"John", #"Peter", ..., #"May"]
The number of NSString is unknown and is taking from an import text file. As NSArray does not support appending new element, how can I create NSArray?
Thanks
UPDATE, let me rephrase the question. How can I create the dynamic array paremeter required by the follow function call?
[segmentedAttributes attributesWithTitlesArray:[NSArray arrayWithObjects:#"John", #"Peter", #"May", nil]]
You don't.
You misunderstand the library behavior.
It is true that there is a convenience constructor arrayWithObjects: which is used thus:
NSArray* array=[NSArray arrayWithObjects:#"Low", #"Medium", #"High", nil];
But this does not create an array with nil at the end. This nil is just to signify the end of the variable-length argument list. It just creates an NSArray with three elements, not four with the last one nil.
You just need to create an NSArray containing the required elements, and pass it to the library function. For example:
NSMutableArray*array=[NSMutableArray array];
while(...){
... get a string ...
[array addObject: string];
}
SCSegmentedAttributes*attributes=[SCSegmentedAttributes attributesWithSegmentTitlesArray:array];
should work, without adding a nil or [NSNull null].
You can't store nil in a Foundation collection class. Instead, you can use [NSNull null]. Use an NSMutableArray, then when you want to add your 'nil' object, use [NSNull null].
Note that when you want to see if an object is [NSNull null] later on, that method will return the same instance every time, so you can do a direct point equality test, like this:
for (id anObject in myArray) {
if (anObject == [NSNull null]) {
NSLog(#"object is nil");
}
else {
NSLog(#"object is not nil: %#", anObject);
}
}
create mutable array then just use
NSString *myString = [NSString stringWithFormat:#"ur new string"];
[myArray addObject:myString];
you have to add object type to array when adding new abject in mutable array.
hope this will help

problem related to NSString

I have 1 NSString *abc = #"Hardik";
i have NSMutableArray *array;
now i had written [array addobject:abc];
then i'm printing,NSLog(#"array = %#", array);
but i'm getting NULL
why?
I have declared NSMutableArray *array; in a.h file
i had set #property(nonatomic,retain)NSMutableArray *array;
#synthesize array;
and i have synthesize it but getting value NULL
I'm not able to understand it?
You also need to initialise your array:
array = [[NSMutableArray alloc] initWithCapacity:10];
This is pretty fundamental stuff. Have you read the "Learning Objective C Primer" yet?
It sounds like you haven't actually allocated array. Generally, you would do this in your initializer. (Don't forget to add a release to your dealloc method, too.) #synthesize creates the getter and setter, but you still have to handle allocating/deallocating the object yourself.
It sounds like your NSMutableArray* array property may not have been initialised?
Can you post your class init method?
To trigger the synthesized accessor within a class itself, you must use self. If you don't, you access the attribute's address directly bypassing the accessor methods. You need:
NSString *abc = #"Hardik";
[self.array addobject:abc];
NSLog(#"array = %#", self.array);
The reason this is important is that the synthesized methods usually also initialize the property. The internals of the synthesize array method would look something like:
-(NSArray *) array{
if (array!=nil) {
return array;
}
array=[[NSMutableArray alloc] initWithCapacity:1];
return array;
}
self.propertyName is really just shorthand for [self propertyName] and self.propertyName=someValue is just shorthand for [self setPropertyName:someValue].
Until you call self.array at least once, the array property is not initialized.
However, just to confuse things, once you have called self.array once it is initialized so you can just call array directly. So...
[self.array addObject:abc];
NSLog(#"array = %#", array);
...works while the converse would return just an empty array.
So the rules are:
Within a class implementation
(including subclasses), calling just
propertyName gives you the address
of the property but does not call
the getter/setter accessor methods.
Within a class implementation
(including subclasses), using
self.propertyName calls the
getter/setter accessor methods but
does not access attribute directly.
From outside the class
implementation e.g.
myClass.propertyName calls the
getter/setter accessor methods.