I have been learning Objective-C for a while, and I decided to try and take on a little bigger project without any real "guidelines" as in the learning books but now I have got stuck.
What I'm trying to do is helping a friend digitalising a few documents he have, by creating a searchable commandline-tool for these documents.
I think I have gotten pretty far, I have created a custom class for documents with three variable; name of the author, number of the article and the path to the file on my computer (which I of course will change to the place he have the documents stored on his computer). I have then created two example documents with all the variables filled in. Since the documents have two properties, numbers and name of the author, the user may search for one of these properties. I therefore separated the input of the user to either be a string or a int (with help of a stack overflow post: How to determine if the first character of a NSString is a letter ) I also created an array with the 'author'-variable's of the different documents.
This is were I have hit a bump: I want to run through the array of the 'author' and if the author's name match with what the user have put in, it will open the document which is at the path given at 'UrlToDoc'. The problem is, the instance variable 'UrlToDoc' is not "connected" to the 'leadAuthor'-variable in some kind of way (as far as I can tell). My question is therefore, how do I, after I have found a match in in the array with what the user written, describe the 'UrlToDoc'-variable for that specific object? (If the user typed in jamesson, for instance, how do I describe the UrlToDoc variable with the value: /Users/pinkRobot435/Desktop/test1.pdf )
Also, if the user writes in a number, the else-statement on the bottom (which would do the same thing) should be used. I haven't written it yet though, but I guess the code for it would be pretty much the same, when describing the 'UrlToDoc'-variable.
Here is my code:
My custom class SMADoc:
SMADoc.h
#import <Foundation/Foundation.h>
#interface SMADoc : NSObject
//Two strings, and a pathway to the documnt, with the purpose of describing the document
#property (nonatomic) int number;
#property (nonatomic) NSString *authour;
#property (nonatomic) NSString *urlToDoc;
#end
SMADoc.m
#import "SMADoc.h"
#implementation SMADoc
#end
main.m
#import <Foundation/Foundation.h>
#import "SMADoc.h"
#include <readline/readline.h>
#include <stdlib.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
SMADoc *one = [[SMADoc alloc] init];
[one setnumber:123];
[one setauthour:#"jamesson"];
[one setUrlToDoc:#"/Users/pinkRobot435/Desktop/test1.pdf"];
SMADoc *two = [[SMADoc alloc] init];
[two setnumber:124];
[two setauthour:#"marc"];
[two setUrlToDoc:#"/Users/pinkRobot435/Desktop/test2.pdf"];
NSMutableArray *authours = [[NSMutableArray alloc] initWithObjects: [one authour], [two authour], nil];
NSLog(#"Enter what you want to search for: ");
const char *searchC = readline(NULL);
NSString *searchOrg = [NSString stringWithUTF8String:searchC];
NSString *search = [searchOrg lowercaseString];
NSRange first = [search rangeOfComposedCharacterSequenceAtIndex:0];
NSRange match = [search rangeOfCharacterFromSet:[NSCharacterSet letterCharacterSet] options:0 range:first];
if (match.location != NSNotFound) {
//The string starts with a letter and the array of authour names should be searched through
for (SMADoc *nSearch in authours) {
if ([search isEqualToString:nSearch]) {
**//Open the file that is represented by UrlToDoc for that specific object**
} else {
NSLog(#"The authour was not found, please try again");
}
}
} else {
//The string starts with a number and should be converted to an int and then the array of numbers (which I have not yet created) should be searched through
int number = atoi(searchC);
}
}
return 0;
}
Thanks in avance!
Instead of your authours array, create an array of SMADoc objects.
Then, in your loop, each object from the array will have an authour or number you can match. When the right one is found, you can just pick the urlToDoc out of the same object.
Currently, you're calling each object in the array a SMADoc * when you examine it, but that's wrong. You created an array of NSString * (and you're comparing it as a string correctly) but what you need there is a real SMADoc *.
Related
#import <Foundation/Foundation.h>
int main (int argc, char * argv[])
{
#autoreleasepool {
NSString *str = #"Programming is fun";
NSLog (#"%#", str);
}
return 0;
}
In the line
NSString *str = #"Programming is fun";
the constant string object Programming is fun is assigned to the NSString variable str. Its value is then displayed using NSLog .
The NSLog format characters %# can be used to display not just NSString objects, but other objects as well.
/*****/
The previous paragraph was from a book I read, what is really confusing to me is why is he keep using the words variable and objects interchangeably? are objects and varaibles the same thing? so far this is the only confusing part about obj-c to me.
please explain, thank you
An object is an instance of a class. Something that is allocated in memory.
A variable is a name which you use to access something like an object (NSString for example) or a primitive (int for example).
In your case so your object is an instance of NSString, that contains #"Programming is fun":
NSString *str = #"Programming is fun";
The variable to access that object is str.
I'm trying to make it so the user input for my code corresponds with the objects in my array, but I have no idea how to do this.
Basically, the assignment says to right a program that will grade final exams, each question has one of four possible answers ( a,b,c,d) and the first answer in the array should correspond to the first question in the exam. The program should them prompt the user for their answers to the exam, and should be compared with the first correct answer in the array and if it matches, it'll give them points.
Problem is, I can't for the life of me figure out how to compare user input to my array to see if it's the right thing they put in.
Here's what I have so far!
I know I'm awful at it, but I'm trying my best. Any help will be extremely appreciated.
#import <Foundation/Foundation.h>
int main (int argc, char * argv[])
{
#autoreleasepool {
NSArray *correctAnswers= [NSArray arrayWithObjects: #"a", #"b ", #"c", #"d",nil];
int sum = 0;
int anwser;
{
NSLog(#"Please input test anwsers starting with 1:");
scanf("%i",&anwser);
}
if ([correctAnswers containsObject:#(anwser)]) {
sum = +10;
}else {
NSLog(#"Well that's not quite right...");
}
NSLog(#"The final score is:%d",sum);
}
return 0;
}
#"a" etc are NSString instances.
You would need to use NSString instance methods to compare them to the user input. You can also do fast enumeration with collections like NSArray
for (NSString* ans in correctAnswers)
{
if ([ans isEqualtoString:[NSString stringWithInt:anwser]]) sum+=10;
}
You could also use NSArray's filteredArrayUsingPredicate: to search the array.
But you can't use containsObject to compare strings... it just checks for a specific NSObject instance.
You also appear to be comparing an int to a char... but that's outside the scope of your question.
I looked around for an answer to this because it seems simple but I could not seem to find something that suffised. I am trying to test a program I have with fake data. I created a StudentData class that possesses a attributes for a student and also has an array to hold them all.
When another class needs them they call a getStudent method (which is the only public part of this class thus far) and sends an ID number to get the name of that student back.
My problem is that I cannot figure out how to parse through the student array to match the student ID numbers with the one that was passed in. Holding the same problem, I cannot figure out how to pull the name out of a student object either.
Here is my code so far:
// StudentData.m
#import "StudentData.h"
#interface StudentData() {
NSString *ID;
NSString *firstName;
}
#property (nonatomic, strong) NSMutableArray *studentArray;
#end
#implementation StudentData
#synthesize studentArray = _studentArray;
- (NSMutableArray *)studentArray {
if(!_studentArray) _studentArray = [[NSMutableArray alloc] init];
return _studentArray;
}
- (void)awakeFromNib {
NSArray *idA = [NSArray arrayWithObjects:#"111", #"685", nil];
NSArray *fnA = [NSArray arrayWithObjects:#"Mark", #"Sam", nil];
for(int i = 0; i < idA.count; i++) {
StudentData *tempCurrentStudent = [[StudentData alloc] init];
ID = [idA objectAtIndex:i];
firstName = [fnA objectAtIndex:i];
[self.studentArray addObject:tempCurrentStudent];
}
}
- (NSString *)getStudentsFirstName:(NSString *)studentID {
NSString *firstName;
for(int i = 0; i < self.studentArray.count; i++) {
if([studentID isEqualToString:[[self.studentArray objectAtIndex:i] self.firstName]]) { // ERROR
fN = [[self.studentArray objectAtIndex:i] self.firstName]; // ERROR
}
}
return firstName;
}
#end
The Error I keep getting is: Expect ']' and the error is pointing to the "self.firstName" lines.
If I use "[self.studentArray objectAtIndex:i].firstName;" I get the error: Property 'firstName' not found on object of type 'id' I even get this error if I make "firstName" a property (both local or public). This makes sense because the array is technically filled with 'id' types. I also know that unlike java I cannot use generics or anything like that.
Any help would be much appreciated and if it needs clarification please let me know! By the way this is for an ipod/ipad app if that helps at all.
Thanks!
Two things :
You're having a name conflict here. You are having a local variable called firstName and the parameter passed to your problem function has the same name.
firstNameis NOT a property unless you define it as one. In this case it's just a normal local variabel. Means that you can't access it using self.firstName but just by firstName. That's one of the reason the coding guidelines say that you should name local variables starting with an underscore ( as you are already doing in the studentArrayproperty). This way you can distinguish if it is a local variable or a property.
Best,
Flo
I have an array of strings, from which I would like to extract only those with unique character sets. (For example, "asdf" and "fdsa" would be considered redundant). This is the method I am currently using:
NSMutableArray *uniqueCharSets = [[NSMutableArray alloc] init];
NSMutableArray *uniqueStrings = [[NSMutableArray alloc] init];
for (NSString *_string in unique) {
NSCharacterSet *_charSet = [NSCharacterSet characterSetWithCharactersInString:_string];
if (![uniqueCharSets containsObject:_charSet]) {
[uniqueStrings addobject:_string];
[uniqueCharSets addObject:_charSet];
}
}
This seems to work, but it's very slow and resource-intensive. Can anyone think of a better way to do this?
Using an NSDictionary, map each string's lexicographically-sorted equivalent to an NSArray of input strings: (e.g. adfs => [afsd, asdf, ...])
Walk through the dictionary, printing out keys (or their values) which only have single-element array values
I just put together a quick example of how I would approach this, but it turns out that it is more, odd, than you first expect. For one, NSCharacterSet doesn't implement equality to check contents. It only uses the pointer value. Based on this your example will NOT work properly.
My approach is to use an NSSet to deal with the hashing of these for us.
#interface StringWrapper : NSObject
#property (nonatomic, copy) NSString *string;
#property (nonatomic, copy) NSData *charSetBitmap;
- (id)initWithString:(NSString*)aString;
#end
#implementation StringWrapper
#synthesize string, charSetBitmap;
- (id)initWithString:(NSString*)aString;
{
if ((self = [super init]))
{
self.string = aString;
}
return self;
}
- (void)setString:(NSString *)aString;
{
string = [aString copy];
self.charSetBitmap = [[NSCharacterSet characterSetWithCharactersInString:aString] bitmapRepresentation];
}
- (BOOL)isEqual:(id)object;
{
return [self.charSetBitmap isEqual:[object charSetBitmap]];
}
- (NSUInteger)hash;
{
return [self.charSetBitmap hash];
}
#end
int main (int argc, const char * argv[])
{
#autoreleasepool {
NSMutableSet *stringWrappers = [[NSMutableSet alloc] init];
NSArray *strings = [NSArray arrayWithObjects:#"abc",#"aaabcccc",#"awea",#"awer",#"abcde", #"ehra", #"QWEQ", #"werawe", nil];
for (NSString *str in strings)
[stringWrappers addObject:[[StringWrapper alloc] initWithString:str]];
NSArray *uniqueStrings = [stringWrappers valueForKey:#"string"];
NSLog(#"%#", uniqueStrings);
}
return 0;
}
The code is pretty straightforward. We create a container object to cache the results of the character set's bitmap representation. We use the bitmap representation because NSData implements isEqual: appropriately.
The only thing that come in my mind is not to use containsObject: since NSMutableArray is not ordered (in general), we can assume that containsObject simply iterates the array starting from the beginning until he finds the object. This means O(n) (n comparisons in the worst case).
A better solution may consists in keeping the array ordered and use a custom search method using a dichotomic approach. This way you'll have a O(log n) complexity.
Of course, you must take care of keeping your array ordered (much more efficient than add and reorder), so you should use insertObject:atIndex: method to insert the element properly.
The following code compiles and runs fine (note the sel_registerName("+")):
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
#interface Integer : NSObject
{
NSInteger intValue;
}
#property (assign) NSInteger intValue;
#end
#implementation Integer
#synthesize intValue;
- (id) plus:(Integer*)anInteger
{
Integer* outInt = [Integer new];
[outInt setIntValue: intValue + [anInteger intValue]];
return outInt;
}
#end
int main (int argc, char const *argv[])
{
id pool = [[NSAutoreleasePool alloc] init];
SEL plusSel = sel_registerName("+");
Method m = class_getInstanceMethod([Integer class], #selector(plus:));
class_addMethod([Integer class], plusSel, method_getImplementation(m), method_getTypeEncoding(m));
Integer* i4 = [Integer new];
Integer* i20 = [Integer new];
[i4 setIntValue: 4];
[i20 setIntValue: 20];
Integer* res = objc_msgSend(i4, plusSel, i20);
NSLog(#"%d + %d = %d", [i4 intValue], [i20 intValue], [res intValue]);
// >> 4 + 20 = 24
[pool drain];
return 0;
}
Other than "yuck", are there reasons to be cautious about doing this?
The API to the ObjC runtime is unlikely to change, but the validity of calling sel_registerName("+") might. I've monkeyed around in the ObjC runtime a lot, and haven't run into any problems even after many updates. That being said, I wouldn't base a multimillion dollar business on this continuing to work forever.
Currently, the Objective-C runtime library doesn't perform any checks on the content of the string you are trying to register and it's unlikely that the development team change that behavior. If it is a non-empty C string, if you always use objc_msgSend to send messages for that selector and if you don't try to do something like [i4 +:i20] (which is going to cause a compiling error), there is no reason to be afraid.
Registered Objective-C selectors are actually C strings stored internally by the runtime system. The runtime system keeps a table of pointers to C strings, the so-called SEL set. When you call sel_registerName the ObjC runtime system calls strcmp for your string and for each C string stored in the SEL set. If any of the C strings in the SEL set is equal to the one you want to register, the function returns the address of the corresponding C string in the set. Otherwise, the system duplicates your string (with strdup), stores the resulting pointer in the SEL set and returns it. This new pointer becomes a new unique selector.