How to check if an object is the last object on NSArray - objective-c

How can I check if an object in the last object on an NSArray?
I've tried:
if ([currentStore isEqual:[Stores lastObject]])
{
//Code
}
but it didn't work.
Any idea?
Thanks!

or try this
BOOL lastElement = false;
NSUInteger index = [stores indexOfObject:currentStore];
if (index != NSNotFound)
{
lastElement = (index == [stores count] - 1);
}

Bit modified try this:
NSUInteger index = [stores indexOfObject:currentStore];
if (index == ([stores count]-1))
{
NSLog(#"Yes its Last");
}

If you didn't override isEqual method, the base class implementation of NSObject::isEqual only check if both pointers points to the same address.
This excellent article http://nshipster.com/equality/ explain objc equality principles
The below sample logs - Testing Stores - works fine
#interface Stores : NSObject
#property (strong, nonatomic) NSString* name;
- (instancetype) initWithName:(NSString*) name;
#end
#implementation Stores
- (instancetype) initWithName:(NSString*) name;
{
_name = name;
return self;
}
- (BOOL)isEqualToStores:(Stores*) Stores
{
if (!Stores)
return NO;
if (![_name isEqualToString:Stores.name] )
return NO;
return YES;
}
- (BOOL)isEqual:(id)object
{
if (self == object)
{
return YES;
}
if (![object isKindOfClass:[Stores class]])
{
return NO;
}
return [self isEqualToStores:(Stores *)object];
}
#end
-(void) testStores
{
Stores* last = [[Stores alloc] initWithName:#"5"];
NSArray* arr = #[
[[Stores alloc] initWithName:#"1"],
[[Stores alloc] initWithName:#"2"],
[[Stores alloc] initWithName:#"3"],
[[Stores alloc] initWithName:#"4"],
[[Stores alloc] initWithName:#"5"]
//last
];
if ([last isEqual:[arr lastObject]])
{
NSLog(#"Testing Stores - works fine");
}
else
{
NSLog(#"Testing Stores - opps!?1?!?");
}
}

Related

Making autocomplete case-insensitive

I'm implementing an autocomplete in cocoa for an OSX application and thus far I've got it all pinned down. The one hangup is that the autocomplete is case-sensitive and that's not really what I want/need. Ideally the autocomplete will be case INSENSITIVE. Relevant code below:
#implementation autocompleteController
- (void)viewDidLoad {
[super viewDidLoad];
self.textField.delegate = self;
}
-(void)controlTextDidChange:(NSNotification *)obj{
NSTextView * fieldEditor = [[obj userInfo] objectForKey:#"NSFieldEditor"];
if (self.isAutocompleting == NO && !self.backspaceKey) {
self.isAutocompleting = YES;
self.lastEntry = [[[fieldEditor string] capitalizedString] copy];
[fieldEditor complete:nil];
self.isAutocompleting = NO;
}
if (self.backspaceKey) {
self.backspaceKey = NO;
}
}
-(NSArray *)control:(NSControl *)control textView:(NSTextView *)textView completions:(NSArray *)words forPartialWordRange:(NSRange)charRange indexOfSelectedItem:(NSInteger *)index{
NSMutableArray * suggestions = [NSMutableArray array];
NSArray * possibleStrings = #[#"TEST", #"ABC", #"abc", #"amauroy", #"AMA", #"amazing"];
if (!self.lastEntry || !possibleStrings) {
return nil;
}
for (NSString * string in possibleStrings) {
if ([string hasPrefix:self.lastEntry]) {
[suggestions addObject:string];
}
}
return suggestions;
}
-(BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector{
if (commandSelector == #selector(deleteBackward:)) {
self.backspaceKey = YES;
}
return NO;
}
#end
As Pro Blaster points out, the problem is with the following line:
if ([string hasPrefix:self.lastEntry]) {
Your autocompletion is case-sensitive because -hasPrefix: is case-sensitive. One approach is to convert everything to lower case (upper case would also work, of course). Another is to write a case-insensitive version of -hasPrefix: and add it to NSString using a category, like this:
#interface NSString (autocomplete)
- (BOOL)hasPrefixIgnoringCase:(NSString*)aString;
#end;
#implementation NSString (autocomplete)
- (BOOL)hasPrefixIgnoringCase:(NSString*)aString
{
NSRange *prefix = [self rangeOfString:aString options:NSCaseInsensitiveSearch];
return prefix.location == 0 && prefix.length == aString.length;
}
#end
Then use that method in your code:
if ([string hasPrefixIgnoringCase:self.lastEntry]) {
Note: The provided code is untested. The concept is sound, but you may find a syntax error or two.
I did this once.
You would do so by replacing :
for (NSString * string in possibleStrings) {
if ([string hasPrefix:self.lastEntry]) {
[suggestions addObject:string];
}
}
return suggestions
with:
for (NSString * string in possibleStrings) {
if ([[string lowercaseString] hasPrefix:[self.lastEntry lowercaseString]]) {
[suggestions addObject:string];
}
}
return suggestions;

How to restrict numbers and special characters in objective-c [duplicate]

This question already has answers here:
Restrict NSTextField to only allow numbers
(10 answers)
Closed 8 years ago.
In textfield I want to restrict numbers like (1234567890) and special characters but I want to allow alphanumeric characters. How I am suppose to do this?
Use the UITextField delegate method
textField:shouldChangeCharactersInRange:replacementString:
To check the string that is about to be replaced, if you allow it then return yes if not then return no.
Here is some more information.
Apple UITextField Delegate
try following code
+ (BOOL)isNumber:(NSString *)value {
if ( (value == nil) || ([#"" isEqualToString:value]) ) {
return NO;
}
int l = [value length];
BOOL b = NO;
for (int i = 0; i < l; i++) {
NSString *str =
[[value substringFromIndex:i]
substringToIndex:1];
const char *c =
[str cStringUsingEncoding:
NSASCIIStringEncoding];
if ( c == NULL ) {
b = NO;
break;
}
if ((c[0] >= 0x30) && (c[0] <= 0x39)) {
b = YES;
} else {
b = NO;
break;
}
}
if (b) {
return YES;
} else {
return NO;
}
}
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if ( (string != nil) && (string != #"") ) {
if (![self isNumber:string]) {
return NO;
}
}
return YES;
}
You need to write a NSFormatter and assign it to your text field. Here an example implementation of a such NSFormatter which uses a NSRegularExpression to validate the NSTextField contents.
#interface XXNameElementFormatter : NSFormatter
#end
#implementation HcNameElementFormatter {
NSRegularExpression *_re;
}
- (id)init {
self = [super init];
if (self) {
[self initRegularExpression];
}
return self;
}
- (void)awakeFromNib
{
[self initRegularExpression];
}
- (void)initRegularExpression
{
NSError *reError;
_re = [NSRegularExpression regularExpressionWithPattern:#"^[a-z]*$" options:NSRegularExpressionCaseInsensitive error:&reError];
NSAssert(_re != nil, #"Error in regular expression, error: %#", reError);
}
- (NSString *)stringForObjectValue:(id)obj
{
return obj;
}
- (BOOL)getObjectValue:(out __autoreleasing id *)obj forString:(NSString *)string errorDescription:(out NSString *__autoreleasing *)error
{
*obj = string;
return YES;
}
- (BOOL)isPartialStringValid:(NSString *__autoreleasing *)partialStringPtr proposedSelectedRange:(NSRangePointer)proposedSelRangePtr originalString:(NSString *)origString originalSelectedRange:(NSRange)origSelRange errorDescription:(NSString *__autoreleasing *)error
{
NSParameterAssert(partialStringPtr != nil);
NSString *partialString = *partialStringPtr;
NSRange firstMatch = [_re rangeOfFirstMatchInString:*partialStringPtr options:0 range:NSMakeRange(0, partialString.length)];
return firstMatch.location != NSNotFound;
}
#end

Archiving objects within objects in objective-c

Thankyou for reading,
PS: I am a beginner so I am not too good at this unfortunetaly, but any help would be very appreciated
So basically I want to archive a big array which contains Account objects, which they themselves contain:
1.a username in form of a NSString,
2.an encrypted password array filled with NSNumbers, and
3.a data array filled with service data objects.
The service data objects have the following:
encrypted serviceType (NSArray filled with NSNumbers) (whatever service the username and password is for)
encrypted username (NSArray filled with NSNumbers)
encrypted password (NSArray filled with NSNumbers)
Now weirdly when trying to archive and save this, I get two errors. One time it won't let me add service data objects to the data array in the Account class anymore, with the following error message (or at least they dont show up in the NSTableView I have, however it does say they exsist):
[<ServiceData 0x60000023bfa0> valueForUndefinedKey:]: this class is not key value
coding-compliant for the key service.
and two, when I try to login in the the username and password from the Account class, it retrieves the username and the first couple and last couple NSNumbers of my password correctly, but the rest of the NSNumbers for the password are in the trillions or something, so I'm wondering what is going wrong, any help would be greatly appreciated.
Here is the code for my instance variables, how I used the NSKeyedArchiver and unarchiver, and how I went about saving and loading the files. Again, please help me, I have been stuck on this for a while and this is kind-of my last resort. I have no idea what is happening!
ACCOUNT CLASS:
H file:
#interface Account : NSObject <NSCoding>
{
#private
NSString *username;
NSMutableArray *password;
NSMutableArray *accData;
}
#property NSString *username;
#property NSArray *password;
FULL M file:
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if(self)
{
username = [aDecoder decodeObjectForKey:#"username"];
password = [aDecoder decodeObjectForKey:#"password"];
accData = [aDecoder decodeObjectForKey:#"data"];
}
return self;
}
-(void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:username forKey:#"username"];
[aCoder encodeObject:password forKey:#"password"];
[aCoder encodeObject:accData forKey:#"data"];
}
SERVICEDATA CLASS:
H file:
#interface ServiceData : NSObject <NSCoding>
{
#private
NSArray* serviceData;
NSArray* usernameData;
NSArray* passwordData;
}
#property NSArray* serviceData;
#property NSArray* usernameData;
#property NSArray* passwordData;
M file:
#import "Account.h"
#import "Crypt.h"
NSMutableArray *accounts;
NSInteger accountNumber = -1;
#implementation Account
#synthesize username;
#synthesize password;
- (id)initWithUsername:(NSString *)name withPassword:(NSMutableArray *)key
{
self = [super init];
if (self)
{
username = name;
password = key;
accData = [[NSMutableArray alloc]init];
}
return self;
}
/*
setters and getters
*/
-(NSString*)getUsername;
{
return username;
}
-(NSArray*)getPassword;
{
return password;
}
-(void)changePassword:(NSMutableArray*)newPassword;
{
NSInteger sizeOldPass = [password count];
NSInteger sizeNewPass = [newPassword count];
int changeXObjects = (int)(sizeNewPass - sizeOldPass);
int changeSize = abs(changeXObjects);
//adjusts size differences
if (changeXObjects < 0)
{
for(int i = 0; i < changeSize; i++)
{
[password removeLastObject];
}
}
else if (changeXObjects > 0)
{
for(int i = 0; i < changeSize; i++)
{
NSNumber *value = [NSNumber numberWithInt:0];
[password addObject:value];
}
}
//change password
NSInteger sizePass = [password count];
for (int k = 0; k < sizePass; k++)
{
[password replaceObjectAtIndex:k withObject:newPassword[k]];
}
}
-(NSMutableArray*)getAccData;
{
return accData;
}
-(void)setAccData:(NSMutableArray*)input
{
[input setArray: accData];
}
+(NSMutableArray*)getAccounts
{
return accounts;
}
+(NSInteger)getAccountNumber
{
return accountNumber;
}
+(void)setAccounts:(id)accs
{
accounts = accs;
}
+(void)setAccountNumber:(NSInteger)number
{
accountNumber = number;
}
/*
other methods
*/
+(void)addAccount:(id)acc
{
[accounts addObject:acc];
}
+(void)deleteAccount:(NSInteger)index
{
[accounts removeObjectAtIndex:index];
}
-(void)addAccData:(id)input
{
[accData addObject:input];
}
-(void)deleteAccDataAt:(NSInteger)index
{
[accData removeObjectAtIndex:index];
}
+(bool)checkPassword:(NSString*)passwordIn accountNumber:(NSInteger)index
{
NSMutableArray *passwordInputCrypt = [Crypt encrypt:passwordIn];
NSMutableArray *passwordCrypt = [accounts[index] getPassword];
NSInteger lengthPassword = [passwordInputCrypt count];
bool correctPassword = true;
if([passwordCrypt count] == [passwordInputCrypt count])
{
for(int i = 0; i < lengthPassword; i++)
{
if(passwordCrypt[i]!=passwordInputCrypt[i])
correctPassword = false;
}
}
else
{
correctPassword = false;
}
if(correctPassword == true)
{
return true;
}
return false;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if(self)
{
username = [aDecoder decodeObjectForKey:#"username"];
password = [aDecoder decodeObjectForKey:#"password"];
accData = [aDecoder decodeObjectForKey:#"data"];
}
return self;
}
-(void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:username forKey:#"username"];
[aCoder encodeObject:password forKey:#"password"];
[aCoder encodeObject:accData forKey:#"data"];
}
#end
LOADING FILE(filePath is given):
NSData *data = [[NSFileManager defaultManager] contentsAtPath:filePath];
if(data != nil)
{
NSArray *arrayFromData = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSMutableArray *initArray = [NSMutableArray arrayWithArray:arrayFromData];
[Account setAccounts:initArray];
}
else
{
NSMutableArray *accountsInit = [[NSMutableArray alloc] init];
[Account setAccounts:accountsInit];
}
SAVING FILE:
NSArray *accounts = [Account getAccounts];
NSString *filePath = [AppController getFilePath];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:accounts];
[data writeToFile:filePath atomically:YES];
A few things:
You should not be archiving password data to disk (even if you are encrypting it). That's what the keychain is for. Have a look at SSKeychain for a good wrapper class.
The Key-value coding error you are getting suggests you are trying to reference your serviceData array as just "service" somewhere. Check your valueForKey and setValueForKey statements.
Can you post the rest of the Account class? That method setAccounts looks like it might be relevant.
Also is there a reason you are using Keyed Archiving instead of Core Data?

Objective-c Priority Queue

I've started using Objective-c for iOS programming. I switched over from Java, and I wanted to know if there were any existing libraries like the Java Collections Framework for Obj-c, more specifically a priority queue implementation. I've done some searches, but have been unable to come up with anything.
UPDATE: I found this, but would have no idea how to use it myself: http://www.ohloh.net/p/pqlib
I was unable to find an implementation of a priority queue, so I went ahead and made my own. I'm not sure how robust it is, but I hope it might point others in the right direction.
PriorityQueue.h
//
// PriorityQueue.h
//
#import <Foundation/Foundation.h>
#import "comparable.h"
//Implements a priority queue. All objects in queue must implement the comparable protocol and must be all of the same type. The queue can be explicity typed at initialization, otherwise the type of the first object entered will be the type of the queue
#interface PriorityQueue : NSObject{
NSMutableArray *queue;
Class type;
}
- (id)init;
- (id)initWithObjects:(NSSet *)objects;
- (id)initWithCapacity:(int)capacity;
- (id)initWithCapacity:(int)capacity andType:(Class)oType; //Queue will reject objects not of that type
#pragma mark - Useful information
- (BOOL)isEmpty;
- (BOOL)contains:(id<comparable, NSObject>)object;
- (Class)typeOfAllowedObjects; //Returns the type of objects allowed to be stored in the queue
- (int) size;
#pragma mark - Mutation
- (void)clear;
- (BOOL)add:(id<comparable, NSObject>)object;
- (void)remove:(id<comparable, NSObject>)object;
#pragma mark - Getting things out
- (id)peek;
- (id)poll;
- (id)objectMatchingObject:(id<comparable, NSObject>)object;
- (NSArray *)toArray;
#pragma mark -
- (void)print;
#end
PriorityQueue.m
//
// PriorityQueue.m
//
#import "PriorityQueue.h"
#define INITIAL_CAPACITY 50
#implementation PriorityQueue
#pragma mark - Initialization
- (id)init{
return [self initWithCapacity:INITIAL_CAPACITY andType:nil];
}
- (id)initWithObjects:(NSSet *)objects{
self = [self initWithCapacity:INITIAL_CAPACITY andType:nil];
for (id<comparable, NSObject>object in objects){
[self add:object];
}
return self;
}
- (id)initWithCapacity:(int)capacity{
return [self initWithCapacity:capacity andType:nil];
}
- (id)initWithCapacity:(int)capacity andType:(Class)oType{
self = [super init];
if(self){
queue = [[NSMutableArray alloc] init];
type = oType;
}
return self;
}
#pragma mark - Useful information
- (BOOL)isEmpty{
if(queue.count == 0){
return YES;
}
else{ return NO;}
}
- (BOOL)contains:(id<comparable, NSObject>)object{
//Search the array to see if the object is already there
for(id<comparable> o in queue){
if([o isEqual:object]){
return YES;
}
}
return NO;
}
- (Class)typeOfAllowedObjects{
NSLog(#"Allowed Types: %#", type);
return type;
}
- (int) size{
return [queue count];
}
#pragma mark - Mutation
//Mutation
- (void)clear{
[queue removeAllObjects];
}
//A "greater" object (compareTo returns 1) is at the end of the queue.
- (BOOL)add:(id<comparable, NSObject>)object{
//Make sure the object's type is the same as the type of the queue
if(type == nil){
// NSLog(#"Type is nil");
type = [object class];
}
if([object class] != type){
NSLog(#"ERROR: Trying to add incorrect object");
return NO;
}
if([queue count] == 0){
[queue addObject:object];
return YES;
}
for(int i = 0; i < [queue count]; i++){
if([object compareTo:queue[i]] < 0){
[queue insertObject:object atIndex:i];
return YES;
}
}
[queue addObject:object];
return YES;
}
- (void)remove:(id<comparable, NSObject>)object{
[queue removeObject:object];
}
#pragma mark - Getting things out
- (id)peek{
return queue[0];
}
- (id)poll{
//Get the object at the front
id head = queue[0];
//Remove and return that object
[queue removeObject:head];
return head;
}
- (id)objectMatchingObject:(id<comparable, NSObject>)object{
//Search the array to see if the object is already there
for(id<comparable> o in queue){
if([o isEqual:object]){
return o;
}
}
return nil;
}
- (NSArray *)toArray{
return [[NSArray alloc] initWithArray:queue];
}
#pragma mark -
- (NSString *)description{
return [NSString stringWithFormat:#"PriorityQueue: %# allows objects of type %#", queue, type];
}
- (void)print{
NSLog(#"%#", [self description]);
}
#end
Comparable.h
//
// comparable.h
//
#import <Foundation/Foundation.h>
//NOTE: Class must check to make sure it is the same class as whatever is passed in
#protocol comparable
- (int)compareTo:(id<comparable, NSObject>)object;
- (BOOL)isEqual:(id<comparable, NSObject>)object;
#end
See https://mikeash.com/pyblog/using-evil-for-good.html where Mike implements Objective-C wrapper for C++ STD priority queue.
CFBinaryHeap can be used as a Priority Queue and is described as such in the docs: https://developer.apple.com/library/mac/documentation/CoreFoundation/Reference/CFBinaryHeapRef/
The downsides seem to be:
1) There is no remove or update element capability. As far as I can tell you can only remove the min element.
2) It is very C-like and not very pleasant to use in Objc or Swift.
My approach supporting value updates. As CFBinaryHeap does not support updating values I put them on an invalidation list and once being extracted the object is inserted again and a new extraction is made.
/**
Objective-C wrapper around CFBinaryHeap implementing a priority queue and extended by updating a previous value
*/
NS_ASSUME_NONNULL_BEGIN
#interface BEPriorityQueue<ObjectType, ValueType> : NSObject
- (void)dispose;
#property (nonatomic, readonly) NSUInteger count;
- (void)insert:(ObjectType)object value:(ValueType)value;
- (void)update:(ObjectType)object value:(ValueType)value;
/** returns and removes object with lowest value (highest priority */
- (ObjectType)extractMinimum;
- (BOOL)containsObject:(ObjectType)object;
- (id)valueForObject:(id)object;
- (void)removeAllObjects;
#end
NS_ASSUME_NONNULL_END
With this implementation:
NS_ASSUME_NONNULL_BEGIN
#interface BEPriorityQueue()
- (CFComparisonResult)compareObject:(id)object1 with:(id)object2;
#end
static CFComparisonResult BEPriorityQueueCompareItems(const void *ptr1, const void *ptr2, void *info)
{
id object1 = (__bridge id)ptr1;
id object2 = (__bridge id)ptr2;
BEPriorityQueue* queue = (__bridge id)info;
return [queue compareObject:object1 with:object2];
}
static const void *BEPriorityQueueItemRetain(CFAllocatorRef allocator, const void *ptr) {
return CFRetain(ptr);
}
static void BEPriorityQueueItemRelease(CFAllocatorRef allocator, const void *ptr) {
CFRelease(ptr);
}
#implementation BEPriorityQueue
{
BOOL _disposed;
CFBinaryHeapRef _binaryHeapRef;
NSMapTable* _objectToValue;
NSMutableSet* _invalidated;
}
- (instancetype)init
{
self = [super init];
if (self)
{
CFBinaryHeapCallBacks callbacks = (CFBinaryHeapCallBacks) {
.version = 0,
.retain = &BEPriorityQueueItemRetain,
.release = &BEPriorityQueueItemRelease,
.copyDescription = &CFCopyDescription,
.compare = &BEPriorityQueueCompareItems
};
CFBinaryHeapCompareContext compareContext = (CFBinaryHeapCompareContext) {
.version = 0,
.info = (__bridge void *)(self),
.retain = NULL,
.release = NULL,
.copyDescription = NULL,
};
_binaryHeapRef = CFBinaryHeapCreate(NULL, 0, &callbacks, &compareContext);
_objectToValue = [NSMapTable strongToStrongObjectsMapTable];
_invalidated = [NSMutableSet set];
}
return self;
}
- (void)dealloc
{
[self dispose];
if (_binaryHeapRef != NULL)
{
CFRelease(_binaryHeapRef);
_binaryHeapRef = NULL;
}
}
- (void)dispose
{
[self removeAllObjects];
_disposed = YES;
}
#pragma mark internal
- (CFComparisonResult)compareObject:(id)object1 with:(id)object2
{
id value1 = [_objectToValue objectForKey:object1];
id value2 = [_objectToValue objectForKey:object2];
return (CFComparisonResult)[value1 compare:value2];
}
#pragma mark interface
- (NSUInteger)count
{
BEEnsureFalse(_disposed);
return (NSUInteger)CFBinaryHeapGetCount(_binaryHeapRef);
}
- (id)extractMinimum
{
BEEnsureFalse(_disposed);
const void *ptr = NULL;
if (!CFBinaryHeapGetMinimumIfPresent(_binaryHeapRef, &ptr))
return nil;
id object = (__bridge id)ptr;
id value = [_objectToValue objectForKey:object];
CFBinaryHeapRemoveMinimumValue(_binaryHeapRef);
[_objectToValue removeObjectForKey:object];
// if the objects was invalidated, it may no longer be the minimum
// therefore reinsert the object and extract again
if ([_invalidated containsObject:object])
{
[_invalidated removeObject:object];
[self insert:object value:value];
return [self extractMinimum];
}
return object;
}
- (void)insert:(id)object value:(id)value
{
BEEnsureFalse(_disposed);
BEEnsureIsNotNil(object);
BEEnsureIsNotNil(value);
BEEnsureTrue([value respondsToSelector:#selector(compare:)]); // <NSComparable>
[_objectToValue setObject:value forKey:object]; // first to be available furing insertion compare
CFBinaryHeapAddValue(_binaryHeapRef, (__bridge void *)object);
}
- (void)update:(id)object value:(id)value
{
BEEnsureFalse(_disposed);
BEEnsureIsNotNil(object);
BEEnsureTrue([value respondsToSelector:#selector(compare:)]); // <NSComparable>
[_objectToValue setObject:value forKey:object]; // first to be available during insertion compare
[_invalidated addObject:object];
}
- (BOOL)containsObject:(id)object
{
BEEnsureFalse(_disposed);
return CFBinaryHeapContainsValue(_binaryHeapRef, (__bridge void *)object);
}
- (id)valueForObject:(id)object
{
return [_objectToValue objectForKey:object];
}
- (void)removeAllObjects
{
CFBinaryHeapRemoveAllValues(_binaryHeapRef);
[_objectToValue removeAllObjects];
[_invalidated removeAllObjects];
}
#end
NS_ASSUME_NONNULL_END

Objective C newbie

I'm taking a class and we're working on a Calculator program. My background is in C++. I am taking a RPN calculator entry of 3 enter sqrt and need to display it as sqrt(3) in my descriptionOfProgram method, which is new, including associated property below. Here's the class so far. Search for "xcode" to find my issues. Any ideas? I'm not very good at the basic objective c classes, but I'm trying to learn. Here's a summary:
it's complaining about my boolean. I'm not sure why. I did this in a different class and it worked fine.
it's looking for a { I don't see it
it doesn't like my use of the key. I'm unclear on how to get the key's contents I think is the problem.
It wants ] but I'm not seeing why
skipped
It expected } at #end
Hope you can help! Thanks!
//
// CalculatorBrain.m
// Calculator
//
// Created by Michele Cleary on 2/25/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "CalculatorBrain.h"
#interface CalculatorBrain()
#property (nonatomic, strong) NSMutableArray *programStack;
#property (nonatomic, strong) NSDictionary *testVariable;
#property (nonatomic) BOOL numberHandledNextOperation;
- (double) convertRadianToDegree: (double) radian;
#end
#implementation CalculatorBrain
#synthesize programStack = _programStack;
#synthesize testVariable = _testVariable;
#synthesize numberHandledNextOperation = _numberHandledNextOperation;
- (NSMutableArray *)programStack
{
if (_programStack == nil) _programStack = [[NSMutableArray alloc] init];
return _programStack;
}
//- (void)setOperandStack:(NSMutableArray *)operandStack
//{
// _operandStack = operandStack;
//}
- (void)pushOperand:(double)operand
{
[self.programStack addObject:[NSNumber numberWithDouble:operand]];
}
- (double)performOperation:(NSString *)operation
{
[self.programStack addObject:operation];
return[CalculatorBrain runProgram:self.program];
}
- (id)program
{
return [self.programStack copy];
}
+ (NSString *)descriptionOfProgram:(id)program
{
self.numberHandledNextOperation = NO; //1. this is a problem with xcode: member reference type struct objc_class * is a pointer; maybe you meant to use ->
NSMutableSet * displayDescrip = [[NSMutableSet alloc] init];
for(id foundItemKey in program)
{
if ([foundItemKey isKindOfClass:[NSString class]])
//operator or variable
{
if ([foundItemKey isEqualToString:#"sin"]&&(!self.numberHandledNextOperation))
{ //2. xcode says To match this {.
NSObject *nextObj = [program objectForKey:(foundItemKey+1); //3. xcode doesn't like this: arithmetic on pointer to interface id which is not a constant size in non-fragile ABI
//[displayDescrip addObject:foundItemKey];
}
else if ([foundItemKey isEqualToString:#"cos"])
{
//[displayDescrip addObject:foundItemKey];
}
else if ([foundItemKey isEqualToString:#"sqrt"])
{
//[displayDescrip addObject:foundItemKey];
}
else if ([foundItemKey isEqualToString:#"Ï€"])
{
//[displayDescrip addObject:foundItemKey];
}
else if (![CalculatorBrain isOperationName:foundItemKey])
{
//variable
//[displayDescrip addObject:foundItemkey];
}
else if (foundItemKey isKindOfClass:[NSNumber class]) //4. xcode expected ]
{
//number
//if next object is operation
if(isOperation([program objectForKey:(foundItemKey+1)))
{
numberHandledNextOperation = YES;
if(isOperationSpecial([program objectForKey:(foundItemKey+1)))
{ //sin or cos or sqrt need parentheses
//[displayDescrip addObject:(foundItemKey+1)];
//[displayDescrip addObject:#"("];
//[displayDescrip addObject:foundItemKey];
//[displayDescrip addObject:#")"];
}
else
{ //regular operation + - / *
//[displayDescrip addObject:(foundItemKey+1)];
//[displayDescrip addObject:(foundItemKey)];
}
numberHandledNextOperation = YES;
} //if
} //else if
} //if
} //for
//not sure if I need this next thing
//NSSet * returnedVarNames = [varNames copy];
//return returnedVarNames;
return #"implement this in Assignment 2";
}
+ (double)runProgram:(id)program
{
NSMutableArray *stack;
if ([program isKindOfClass:[NSArray class]]) {
stack = [program mutableCopy];
}
return [self popOperandOffStack:stack];
}
+ (double)runProgram:(id)program usingVariableValues:(NSDictionary *)variableValues
{
NSMutableArray *stack;
if ([program isKindOfClass:[NSArray class]]) {
stack = [program mutableCopy];
}
if(variableValues)
{
int numItemsDisplayed = [stack count];
for (int count = 0; count < numItemsDisplayed; count++)
{
id foundItem = [stack objectAtIndex:count];
if ([foundItem isKindOfClass:[NSString class]])
{
NSString * var = [variableValues objectForKey:foundItem];
if(var)
{
[stack replaceObjectAtIndex:count withObject:[NSNumber numberWithDouble:[var doubleValue]]];
}
}
}
}
return [self popOperandOffStack:stack];
}
+ (double)popOperandOffStack:(NSMutableArray *)stack
{
double result = 0;
id topOfStack = [stack lastObject];
if (topOfStack) [stack removeLastObject];
if([topOfStack isKindOfClass:[NSNumber class]]){ //number
result = [topOfStack doubleValue];
}
else if ([topOfStack isKindOfClass:[NSString class]]){ //string operation
NSString *operation = topOfStack;
if ([operation isEqualToString:#"+"]) {
result = [self popOperandOffStack:stack] + [self popOperandOffStack:stack];
}else if ([operation isEqualToString:#"*"]) {
result = [self popOperandOffStack:stack] * [self popOperandOffStack:stack];
}else if ([operation isEqualToString:#"/"]) {
double divisor = [self popOperandOffStack:stack];
if (divisor)
result = [self popOperandOffStack:stack] / divisor;
}else if ([operation isEqualToString:#"-"]) {
double subtrahend = [self popOperandOffStack:stack];
result = [self popOperandOffStack:stack] - subtrahend;
}else if ([operation isEqualToString:#"sin"]) {
result = result = (sin([self popOperandOffStack:stack])); //(sin([self convertRadianToDegree:[self popOperandOffStack:stack]]));
}else if ([operation isEqualToString:#"cos"]) {
result = (cos([self popOperandOffStack:stack]));
}else if ([operation isEqualToString:#"sqrt"]) {
result = (sqrt([self popOperandOffStack:stack]));
}else if ([operation isEqualToString:#"π"]) {
result = M_PI;
}else{
result = 0;
}
}
return result;
}
+ (NSSet *)variablesUsedInProgram:(id)program
{
NSMutableSet * varNames = [[NSMutableSet alloc] init];
for(id foundItem in program)
{
if ([foundItem isKindOfClass:[NSString class]])
{
if (![CalculatorBrain isOperationName:foundItem])
{
[varNames addObject:foundItem];
}
}
}
NSSet * returnedVarNames = [varNames copy];
return returnedVarNames;
}
+ (BOOL)isOperationName:(NSString *)foundItem
{
NSSet *myOperationSet = [NSSet setWithObjects:#"sqrt", #"sin", #"cos", #"π", #"+", #"-", #"*", #"/", nil];
return([myOperationSet containsObject:(foundItem)]);
}
- (NSString *)description
{
return [NSString stringWithFormat:#"stack = %#", self.programStack];
}
-(double) convertRadianToDegree: (double) radian;
{
return M_PI*2*radian/360;
}
#end //6. xcode expected }
+ (NSString *)descriptionOfProgram:(id)program
Do you actually want descriptionOfProgram a class + method ? If yes, it is more like a static method in C++. It doesn't belong to any particular instance of a class. There is no hidden parameter of constant pointer to the current instance is passed.