Accessing Attributes - objective-c

I am new to programming and objective C, sorry I have to ask this basic question. I can not figure out the lines of code below. Why do I need to use & sign for the range when assess attributes? but not use it when I call the attribute again? is it because the first one is a setter and second one is getter?
Thanks for your advises in advances!
-(NSAttributedString*)characterWithAttribute: (NSString*)attributeName{
NSMutableAttributedString* characters = [[NSMutableAttributedString alloc]init];
int index = 0;
while(index < [self.textToAnalyze length]){
NSRange range;
id value = [self.textToAnalyze attribute:attributeName atIndex:index effectiveRange:&range];
if(value){
[characters appendAttributedString:[self.textToAnalyze attributedSubstringFromRange:range]];
index = (int)range.location + (int)range.length;
}
else{
index++;
}
}
return characters;
}

is it because the first one is a setter and second one is getter?
Basically, yes. The ampersand specifies the address of the variable rather than the value. When the called routine has an address, it can assign a value to it for you to use when it returns. If the called routine only had a value, it wouldn't be able to replace it in any way that was accessible to the caller.
Here's a link that might give you more in-depth details:
https://www.codingunit.com/c-tutorial-call-by-value-or-call-by-reference

Related

set ivars from NSDictionnary

I'm currently working on a project where the user defines some parameters in a NSDictionnary, that I'm using to setup some objects.
For example, you can ask to create a Sound object with parameters param1=xxx, param2=yyy, gain=3.5 ... Then an Enemi object with parameters speed=10, active=YES, name=zzz ...
{
active = NO;
looping = YES;
soundList = "FINAL_PSS_imoverhere_all";
speed = 100.0;
}
I then instantiate my classes, and would like to set the ivars automatically from this dictionnary.
I've actually wrote some code to check that this parameter exists, but I'm having trouble in actually setting the parameter value, especially when the parameter is non object (float or bool).
Here's what I'm doing so far :
//aKey is the name of the ivar
for (NSString *aKey in [properties allKeys]){
//create the name of the setter function from the key (parameter -> setParameter)
NSString *setterName = [aKey stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[aKey substringToIndex:1] uppercaseString]];
setterName = [NSString stringWithFormat:#"set%#:",setterName];
SEL setterSelector = NSSelectorFromString(setterName);
//Check if the parameter exists
if ([pge_object respondsToSelector:setterSelector]){
//TODO : automatically set the parameter
}
else{
[[PSMessagesChecker sharedInstance]logMessage:[NSString stringWithFormat:#"Cannot find %# on %#", aKey, [dict objectForKey:#"type"]] inColor:#"red"];
NSLog(#"Cannot find %# on %#", aKey, [dict objectForKey:#"type"]);
}
}
}
As you can see, I don't know what to do once I've found that the parameter exists on the object. I tried to use "performSelector... withObject..., but my problem is that some of the parameters are non-objects (float or bool).
I also tried to get the class of the parameter, by using the setter, but it didn't help.
Did anyone manage to do something like that?
Jack Lawrence's comment is spot on.
What you are looking for is called Key Value Coding, or just KVC.
This fundamental part of Cocoa lets you get and set any instance variable using its name as a String and a new value.
It will automatically handle coercing Objects to primitive values, so you can use it for int and float properties too.
There is also support for validating values and handling unknown properties.
see the docs
your code, without validation, could be written
for( id eachKey in props ) {
[anOb setValue:props[eachKey] forKey:eachKey];
}
or just
[anOb setValuesForKeysWithDictionary:props];
as Jack said.
For the non-object parameters you have to put them into an object, for example NSNumber or NSValue. You can then add these objects into your dictionary.
For Example:
float f = 0.5;
NSNumber f_obj = [NSNumber numberWithFloat:f];

Recursivity in methods, Algorithm and NSValue issue

-(BOOL)isInArray:(CGPoint)point{
if ([valid count]==0) {
return NO;
}
for (NSValue *value in valid) {
CGPoint er=[value CGPointValue];
if( CGPointEqualToPoint(point,er)) return NO;
}
return YES;
}
-(void)check:(CGPoint)next{
if (!next.y==0) {
int ics=(int) next.x;
int igrec=(int)next.y;
if (mat[ics][igrec]==mat[ics-1][igrec]){
if (![self isInArray:next]) {
[valid addObject:[NSValue valueWithCGPoint:next]];
NSLog(#"valid y!=0 : %#",valid);
[self check:CGPointMake(ics-1, igrec)];
}
}
}
}
y are columns , x are rows , mat is a C matrix
what i'm trying to do here is this:i get a point, next, in a matrix,mat (i'll use struct but for the scope of testing i use CGPoint..it's basicaly the same thing), and for that point i check if it's on the first row and if it's not i check if the value is equal to the value of the row above .If it is, i add the coord of the point into an array and move to the value above (recursively). I have ifs for left,right, and below too...but the idea is the same.
My issues:
For some reason it doesn't work as it should, even with a mat full of 1 values
The NSMutableArray i use to store the points is always null (note that the NSLog gets called so it should've added an object already)
Does recursivity work with methods?
If you have a better idea how to do this...i'm listening
The "valid" array is nil because you haven't allocated it. (You can send an addObject: message, or any message, to a nil pointer--it just doesn't do anything.) Make sure you've got
valid = [[NSMutableArray alloc] init];
somewhere before you're calling this code.
Also, "!next.y==0" is questionable. It might turn out to be identical to "next.y != 0" even if ! has a higher precedence that ==, but I wouldn't guarantee it. That's all I spot for now, without really grokking what this code is trying to do..
Oh, another quick note: Instead of writing your own isInArray, just use NSArray's containsObject:. The inner part of the check method (second indent) is then
NSValue* pointVal = [NSValue valueWithCGPoint:next];
if ( ![valid containsObject:pointVal] )
{
[valid addObject:next];
[self check:CGPointMake(ics-1, igrec)];
}
Or, if you don't care about the order of the points in the valid array you could use an NSMutableSet instead and not worry about checking if the point is already in the collection.
And yes, recursion in methods is fine. They're really the same as C functions, just with a couple hidden arguments (the self pointer and the method name) and called through a dispatch function.

Objective-C: Comparing array of strings to user-entered string, then returning appropriate value?

This is a question about iOS programming with Objective C.
I have an NSMutableArray of strings "csvContent", that were parsed from a CSV file that contained a pseudo-database of questions, answers, and keywords. The contents of the CSV file were as follows:
ID#, "Here is the question I am asking?", "[question, key, words]", "This is the answer to your question."
There are about 2,000 of these questions and related keywords and answers, and I have successfully parsed them into the array, line by line so that each element contains everything in the example you see above.
My question is that if I want to have a user ask a question in a UITextField and then compare the UserQuestion and find the most similar question in my array of strings and then return its answer, what would be the best way to go about doing so? I've looked through documentation about Levenshtein distance and think that that would be a good option, but don't know how to exactly implement it and have it iterate through my entire CSVContent array. I'm not looking for exact code, but an ideal answer would contain some pseudocode or methodology on how to go about this.
To summarize:
Array of strings, CSVContent, of appearance: [id,"question",("question keywords"),"answer"].
I have a UITextField where I can parse a user's entered question into a string UserQuestion.
I want to use a fast comparison algorithm (Levenshtein?) to compare UserQuestion to the elements inside CSVContent and find the appropriate question and related answer, then return the answer.
When user hits the Search button, pass textField.text to this method:
- (int)matchingIDForString:(NSString *)userSuppliedText {
NSInteger bestLevDistanceSoFar = 9999999;
int indexOfMatch=-1;
// having to search the entire array each time is, of course, scary.
for ( int j=0; j<[myMutableArray count]; j++ ) {
NSString *candidateAnswer = [myMutableArray objectAtIndex:j];
// if candidateAnswer is a string, just use it. Else extract the member you want...
NSInteger *levDistance = [self myLevensteinDistanceMethod:candidateAnswer
forstring:userSuppliedText];
if ( levDistance < bestLevDistanceSoFar ) {
indexOfMatch = j;
bestLevDistanceSoFar = levDistance;
}
}
return indexOfMatch; // called should test for <0 meaning no match
}
You'll need to implement this method also:
- (NSInteger *)myLevensteinDistanceMethod:(NSString *)string1 forString:(NSString *)string2 {
// calculate the lev distance, and return it as an NSInteger *.
}

If "a == b" is false when comparing two NSString objects

I have a class with an accessible method that passes back an NSString when called.
[MyClass getMyString]
The string variable in that class is actually assigned in the didSelectRowAtIndexPath: part of a table like this:
myString = cell.textLabel.text;
When I retrieve the string by calling that method, I assign it to another string in the class that called it and compare it to a string I have defined
NSString *mySecondString;
mySecondString = #"my value";
if(mySecondString == myString){
i = 9;
}
I have stepped through the code and every time it evaluates the if statement, it skips right past the i=9 and goes to the next else if statement. Why would this be? Why don't they evaluate to be the same value? If you hover your cursor over each of the values during debugging they will show they have the same value, but the code for some reason with not do as I expect it to do and assign 9 to i.
Any thoughts?
You're assuming that the C == operator does string equality. It doesn't. It does pointer equality (when called on pointers). If you want to do a real string equality test you need to use the -isEqual: method (or the specialization -isEqualToString: when you know both objects are strings):
if ([mySecondString isEqualToString:myString]) {
i = 9;
}
You are comparing pointers to strings, rather than the strings themselves. You need to change your code to
if (if([mySecondString isEqualToString:myString]) {
....
}
you can not use '==' to compare two NSString
you should to use [NSString isEqualToString:(NSString*)] to compare two string
You can not compare the two string using "==" this is for int and other values.
you can use below code for the comparing two string
if ([Firststring isEqualToString:Secondstring]) {
NSLog(#"Hello this both string is same ");
}
It's a basic concept of pointer, you are missing. (YES, myString and mySecondString are pointers to the string).
Now, if(mySecondString == myString) will go TRUE only if, both the pointers are pointing to the same location. (Which they won't in most cases)
You should be doing if ([mySecondString isEqualToString:myString]), which will be comparing your both string's content for equality.

Objective-C modify parameter to method at runtime

I'd like to change the value of a parameter at runtime and curious how this works in Obj-C.
I have a loop where value of 'n' is 0 increasing by 1 with each loop. How does one increment the passed parameter by 1 as n moves.
UIViewSubclass *uiViewSubclass = [[UIViewSubclass alloc] initWithValue:([value integerValue])
andPlacement:kPlacement0;
next time through loop I'd like 2nd argument to read as: andPlacement:kPlacement1;
and then: andPlacement:kPlacement2; and on...
do I make kPlacement a string and stringByAppendingString:[[NSNumber numberWithInt:n] stringValue]; ?
What's the Obj-C/Cocoa approach?
You can't modify your source code or make up variable references at runtime. Objective-C isn't that dynamic.
If the values of kPlacement0 through kPlacementMax are sequential, you may be able to use a for loop to step through them directly:
for (MyPlacement placement = kPlacement0; placement += kPlacementIncrement; placement <= kPlacementMax) {
UIViewSubclass *instanceOfUIViewSubclass = [[UIViewSubclass alloc] initWithValue:([value integerValue])
andPlacement:placement];
//Do something with instanceOfUIViewSubclass.
[instanceOfUIViewSubclass release];
}
(You will need to define kPlacementIncrement and kPlacementMax in addition to the kPlacement0, etc. constants. I'm using MyPlacement as the name of your enumeration type that the kPlacement0 etc. constants correspond to.)
If they are not sequential, put them in a C array and iterate on that array:
enum { numPlacements = <#Insert the number of placement constants here#> };
MyPlacement placements[numPlacements] = {
kPlacement0,
kPlacement1,
kPlacement2,
⋮
}
for (unsigned i = 0U; i < numPlacements; ++i) {
UIViewSubclass *instanceOfUIViewSubclass = [[UIViewSubclass alloc] initWithValue:([value integerValue])
andPlacement:placements[i]];
//Do something with instanceOfUIViewSubclass.
[instanceOfUIViewSubclass release];
}
You probably can come up with more-descriptive names than kPlacement0 etc. When you want to refer to them by number, do that; when you want to refer to them by name, give them good names.