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
Related
I'm trying to create a DownloadOperation subclass of NSOperation to download data asynchronously. Everything seemed to be working fine until I tried to add cancelling support. Basically, the completion handler of the operation's NSURLSessionDownloadTask seems to be called after the operation has been released. It will crash with EXC_BAD_ACCESS at the line weakSelf.state = kFinished.
The full sample project is here: https://github.com/angstsmurf/DownloadOperationQueue. Press Command+. after running to crash.
#import "DownloadOperation.h"
typedef enum OperationState : NSUInteger {
kReady,
kExecuting,
kFinished
} OperationState;
#interface DownloadOperation ()
#property NSURLSessionDownloadTask *task;
#property OperationState state;
#end
#implementation DownloadOperation
// default state is ready (when the operation is created)
#synthesize state = _state;
- (void)setState:(OperationState)state {
#synchronized(self) {
if (_state != state) {
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
_state = state;
[self didChangeValueForKey: #"isExecuting"];
[self didChangeValueForKey: #"isFinished"];
}
}
}
- (OperationState)state {
#synchronized (self) {
return _state;
}
}
- (BOOL)isReady { return (self.state == kReady); }
- (BOOL)isExecuting { return (self.state == kExecuting); }
- (BOOL)isFinished { return (self.state == kFinished); }
- (BOOL)isAsynchronous {
return YES;
}
- (instancetype)initWithSession:(NSURLSession *)session downloadTaskURL:(NSURL *)downloadTaskURL completionHandler:(nullable void (^)(NSURL * _Nullable, NSURLResponse * _Nullable, NSError * _Nullable))completionHandler {
self = [super init];
if (self) {
__unsafe_unretained DownloadOperation *weakSelf = self;
// use weak self to prevent retain cycle
_task = [[NSURLSession sharedSession] downloadTaskWithURL:downloadTaskURL
completionHandler:^(NSURL * _Nullable localURL, NSURLResponse * _Nullable response, NSError * _Nullable error) {
/*
if there is a custom completionHandler defined,
pass the result gotten in downloadTask's completionHandler to the
custom completionHandler
*/
if (completionHandler) {
completionHandler(localURL, response, error);
}
/*
set the operation state to finished once
the download task is completed or have error
*/
weakSelf.state = kFinished;
}];
}
return self;
}
- (void)start {
/*
if the operation or queue got cancelled even
before the operation has started, set the
operation state to finished and return
*/
if (self.cancelled) {
self.state = kFinished;
return;
}
// set the state to executing
self.state = kExecuting;
NSLog(#"downloading %#", self.task.originalRequest.URL.absoluteString);
// start the downloading
[self.task resume];
}
-(void)cancel {
[super cancel];
// cancel the downloading
[self.task cancel];
}
#end
As pointed out in the comments by Scott Thompson, the correct keyword to use for the weakSelf variable is __weak, not __unsafe_unretained.
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?!?");
}
}
I've been investigating NSProgress but have found the existing documentation, class reference and tutorials to be lacking. I'm mainly wondering if my NSProgress is applicable to my use case. The class reference documentation alternatively refers to suboperations or subtasks, I may be mistaken but I interpreted suboperations to mean a case where an NSOperation manages a group of other NSOperations. An example of my use case is as follows:
Create an Upload All Items in Group operation for each group that exists.
Add each of these operations to an NSOperationQueue.
Each Upload All Items in Group operation will create an Upload Item operation for each item in their group. These all get added to an NSOperationQueue managed by the operation.
I would have expected NSProgress to support this, and allow me to propagate progress from the nested operations (Upload Item operation) to the parent operation, and then finally to the main thread and the UI. But I've had difficulty implementing this, it seems as though NSProgress is meant more for long operations that execute all their code on one background thread, but have separate "sections" that make it easy to determine when progress has been made, if this is the case then the use of the term suboperation is a bit misleading as it brings to mind the use of nested NSOperations.
Thank you for any help you can provide, and let me know if additional details are needed.
NSProgress knows nothing about NSOperations -- the two things are orthogonal -- but that doesn't mean it can't be used with them. The idea behind nesting NSProgress "tasks" is that the inner task doesn't know anything about the outer task, and the outer task doesn't need direct access to the inner task's NSProgress to pull in updates for it. I cooked up a little example:
// Outer grouping
NSProgress* DownloadGroupsOfFiles(NSUInteger numGroups, NSUInteger filesPerGroup)
{
// This is the top level NSProgress object
NSProgress* p = [NSProgress progressWithTotalUnitCount: numGroups];
for (NSUInteger i = 0; i < numGroups; ++i)
{
// Whatever DownloadFiles does, it's worth "1 unit" to us.
[p becomeCurrentWithPendingUnitCount: 1];
DownloadFiles(filesPerGroup);
[p resignCurrent];
}
return p;
}
// Inner grouping
void DownloadFiles(NSUInteger numberOfFiles)
{
NSProgress* p = [NSProgress progressWithTotalUnitCount: numberOfFiles];
NSOperationQueue* opQueue = [[NSOperationQueue alloc] init];
// Make the op queue last as long as the NSProgress
objc_setAssociatedObject(p, NULL, opQueue, OBJC_ASSOCIATION_RETAIN);
// For each file...
for (NSUInteger i = 0; i < numberOfFiles; ++i)
{
// Whatever this DownloadOperation does is worth 1 "unit" to us.
[p becomeCurrentWithPendingUnitCount: 1];
// Make the new operation
MyDownloadOperation* op = [[MyDownloadOperation alloc] initWithName: [NSString stringWithFormat: #"File #%#", #(i+1)]];
[opQueue addOperation: op];
[p resignCurrent];
}
}
// And then the DownloadOperation might look like this...
#interface MyDownloadOperation : NSOperation
#property (nonatomic, readonly, copy) NSString* name;
- (id)initWithName: (NSString*)name;
#end
#implementation MyDownloadOperation
{
NSProgress* _progress;
NSString* _name;
}
- (id)initWithName:(NSString *)name
{
if (self = [super init])
{
_name = [name copy];
// Do this in init, so that our NSProgress instance is parented to the current one in the thread that created the operation
_progress = [NSProgress progressWithTotalUnitCount: 1];
}
return self;
}
- (void)dealloc
{
_name = nil;
_progress = nil;
}
- (void)main
{
// Fake like we're doing something that takes some time
// Determine fake size -- call it 768K +- 256K
const NSUInteger size = 512 * 1024 + arc4random_uniform(512*1024);
const NSUInteger avgBytesPerSec = 1024 * 1024;
const NSTimeInterval updatePeriod = 1.0/60.0;
// Make sure all the updates to the NSProgress happen on the main thread
// in case someone is bound to it.
dispatch_async(dispatch_get_main_queue(), ^{
_progress.totalUnitCount = size;
_progress.completedUnitCount = 0;
});
NSUInteger bytesRxd = 0;
do
{
// Sleep for a bit...
usleep(USEC_PER_SEC * updatePeriod);
// "Receive some data"
NSUInteger rxdThisTime = updatePeriod * avgBytesPerSec;
// Never report more than all the bytes
bytesRxd = MIN(bytesRxd + rxdThisTime, size);
// Update on the main thread...
dispatch_async(dispatch_get_main_queue(), ^{
[_progress setCompletedUnitCount: bytesRxd];
});
} while (bytesRxd < size);
}
#end
One thing to note is that if NSProgress is being used to convey status to the UI, then you will want to make sure that every time you update the NSProgress object, you do so from the main thread, otherwise you'll get lots of weird crashes.
Alternately you could just use NSURLConnection to download files, and then have a delegate like this:
#interface MyURLConnectionProgressReporter : NSObject <NSURLConnectionDownloadDelegate>
#property (nonatomic, readwrite, assign) id<NSURLConnectionDownloadDelegate> delegate;
#end
NSProgress* DownloadABunchOfFiles(NSArray* arrayOfURLs)
{
arrayOfURLs = arrayOfURLs.count ? arrayOfURLs : #[ [NSURL URLWithString: #"http://www.google.com"] ];
NSProgress* p = [NSProgress progressWithTotalUnitCount: arrayOfURLs.count];
for (NSURL* url in arrayOfURLs)
{
[p becomeCurrentWithPendingUnitCount: 1];
MyURLConnectionProgressReporter* delegate = [[MyURLConnectionProgressReporter alloc] init];
NSURLConnection* conn = [[NSURLConnection alloc] initWithRequest: [NSURLRequest requestWithURL: url] delegate: delegate];
[conn start];
[p resignCurrent];
}
return p;
}
#implementation MyURLConnectionProgressReporter
{
NSProgress* _progress;
}
static void EnsureMainThread(dispatch_block_t block);
- (id)init
{
if (self = [super init])
{
_progress = [NSProgress progressWithTotalUnitCount: 1];
EnsureMainThread(^{
_progress.kind = NSProgressKindFile;
[_progress setUserInfoObject:NSProgressFileOperationKindDownloading forKey:NSProgressFileOperationKindKey];
});
}
return self;
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
id retVal = [super forwardingTargetForSelector:aSelector];
if (!retVal && [self.delegate respondsToSelector: _cmd])
{
retVal = self.delegate;
}
return retVal;
}
- (void)p_updateWithTotalBytesWritten:(long long)totalBytesWritten expectedTotalBytes:(long long) expectedTotalBytes
{
// Update our progress on the main thread...
EnsureMainThread(^{
if (!expectedTotalBytes)
_progress.totalUnitCount = -1;
else
_progress.totalUnitCount = MAX(_progress.totalUnitCount, expectedTotalBytes);
_progress.completedUnitCount = totalBytesWritten;
});
}
- (void)connection:(NSURLConnection *)connection didWriteData:(long long)bytesWritten totalBytesWritten:(long long)totalBytesWritten expectedTotalBytes:(long long) expectedTotalBytes
{
// Update our progress
[self p_updateWithTotalBytesWritten: totalBytesWritten expectedTotalBytes: expectedTotalBytes];
// Then call on through to the other delegate
if ([self.delegate respondsToSelector: _cmd])
{
[self.delegate connection:connection didWriteData:bytesWritten totalBytesWritten:totalBytesWritten expectedTotalBytes:expectedTotalBytes];
}
}
- (void)connectionDidResumeDownloading:(NSURLConnection *)connection totalBytesWritten:(long long)totalBytesWritten expectedTotalBytes:(long long) expectedTotalBytes
{
// Update our progress
[self p_updateWithTotalBytesWritten: totalBytesWritten expectedTotalBytes: expectedTotalBytes];
// Then call on through to the other delegate
if ([self.delegate respondsToSelector: _cmd])
{
[self.delegate connectionDidResumeDownloading:connection totalBytesWritten:totalBytesWritten expectedTotalBytes:expectedTotalBytes];
}
}
- (void)connectionDidFinishDownloading:(NSURLConnection *)connection destinationURL:(NSURL *) destinationURL
{
// We're done, so we want (_progress.completedUnitCount == _progress.totalUnitCount)
EnsureMainThread(^{
_progress.completedUnitCount = _progress.totalUnitCount;
});
if ([self.delegate respondsToSelector: _cmd])
{
[self.delegate connectionDidFinishDownloading:connection destinationURL:destinationURL];
}
}
static void EnsureMainThread(dispatch_block_t block)
{
if (!block)
return;
else if ([NSThread isMainThread])
block();
else
dispatch_async(dispatch_get_main_queue(), block);
}
#end
Hope that helps.
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.
I came into a situation where I had to write a loop with a good amount if iterations and in this loop I had a NSData object that I had to associate with a key. This lead me to search for a simple objective-c _KeyValuePair_ class but coulnt not find one so I wrote my own. Now I'm curious to see if there is any benefit over just using an NSMutableDictinoary holding just 1 key and value. After trying both throughout my project I can't tell much difference on the App UI side or with Instruments Time Profiler.
So my questions are:
Could a single kvpair class be more efficient than a NSMutableDictionary
Does a NSMutableDict allocate any larger amount of space by default then this does
Is there actually a standard single key value pair class that I just missed
Some code:
for (int i = 0, count = [photoUrls count]; i < count; ++i) {
// Example usage of the kvp class
NSMutableDictionary *imageRequest = [[NSMutableDictionary alloc] init];
JHKeyValuePair *kvPair = [[JHKeyValuePair alloc] initWithKey:#"DAILY" andValue:[NSNumber numberWithInt:i];
[imageRequest setObject:self forKey:#"delegate"];
[imageRequest setObject:kvPair forKey:#"userInfo"];
[kvPair release];
[imageRequest setObject:[dailySpecialData objectForKey:#"IMAGE_URL"] forKey:#"url"];
[imageDownloader addDownloadRequestToQueue:imageRequest];
[imageRequest release];
}
JHKeyValuePair.h
#interface JHKeyValuePair : NSObject {
id key;
id value;
}
#property (nonatomic, retain) id key;
#property (nonatomic, retain) id value;
- (id)initWithKey:(id)aKey andValue:(id)aValue;
#end
JHKeyValuePair.m
#import "JHKeyValuePair.h"
#implementation JHKeyValuePair
#synthesize key;
#synthesize value;
- (id)initWithKey:(id)aKey andValue:(id)aValue {
if ((self = [super init])) {
key = [aKey retain];
value = [aValue retain];
}
return self;
}
- (void)dealloc {
[key release], key = nil;
[value release], value = nil;
[super dealloc];
}
- (id)copyWithZone:(NSZone *)zone {
JHKeyValuePair *copy = [[JHKeyValuePair allocWithZone:zone] init];
[copy setKey:self.key];
[copy setValue:self.value];
return copy;
}
- (BOOL)isEqual:(id)anObject {
BOOL ret;
if (self == anObject) {
ret = YES;
} else if (![anObject isKindOfClass:[JHKeyValuePair class]]) {
ret = NO;
} else {
ret = [key isEqual:((JHKeyValuePair *)anObject).key] && [value isEqual:((JHKeyValuePair *)anObject).value];
}
return ret;
}
#end
Edit to fix the initial explanation. Seems I got sidetracked mid-sentance and never came back to finish it.
If you really want to get speed you are doing a lot of unnecessary retain releases that probably aren't necessary every time you set your key/values. If you use a struct and some basic c code you can achieve something a little quicker but you sacrifice the simple and consistent memory management you get from doing it the objective c way.
typedef struct {
id key;
id value;
} Pair;
BOOL isEqual(Pair a, Pair b); //...
// You will need to clean up after yourself though:
void PairRelease(Pair p) {
[p.key release];
[p.value release];
}