NSMutableArray subclass init - objective-c

I'm trying to make a function for a NSMutableArray subclass that only uses integer, but I don't want to use "count." How do I do this?
-(NSMutableArrayWithIntegers*)initWithCount:(NSInteger)count numbers:(NSInteger)firstInt, ...
{
self = [super init];
if (self) {
va_list args;
va_start(args, firstInt);
NSInteger arg = firstInt;
for (int i = 0; i < count; i++)
{
arg = va_arg(args, NSInteger);
[self addObject: [NSNumber numberWithInteger:arg]];
}
va_end(args);
}
return self;
}

I know this doesn't answer your question but it's important to let you know. Don't ever subclass NSMutableAnything. Use a category and thank me later:
#interface NSMutableArray (ListOfIntegers)
+(NSMutableArray)mutableArrayWithIntegers:(NSInteger)i, ... {
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:whatever];
// do your thing
return array;
}
#end

First of all, the approach you currently have is just fine. Don't try getting rid of the count. There are alternatives, but they are only worse.
For example, you may use a sentinel value (which may not be inserted into the array) as the last argument, but in this case, you will have to make sure that you are not actually trying to insert this value to the array at all:
- (id)initWithIntegers:(NSInteger)first, ...
{
if (!(self = [super init])) return nil;
va_list args;
va_start(args, first);
NSInteger n;
if (first != NSIntegerMax) {
[self addObject:#(first)];
while ((n = va_arg(args, NSInteger)) != NSIntegerMax) {
[self addObject:#(n)];
}
}
va_end(args);
return self;
}
But really, this unnecessarily narrows the range of values that can be added - using that count argument is not a big deal.

Related

Using method parameters check to see if array object is equal to another string (objective-C)?

I'm all self taught so please keep the techincal jargin to a minimum. Theoretically if I had a method and it had a parameter that is the name of an array. How do I check to see if that array index 5 is equal to #"Yes" or #"No". I know it's one of these because its testing to see if the picture is appearing in the veiw controller. Here is an example:
-(void)methodName :(NSMutableArray *)arrayNameInMethod {
if ( [NSMutableArray *(arrayNameInMethod) indexOfObject:5] == #"Yes"){
//Hide a different picture assocciated with the Array
} else {
//Unhide a different picture assocciated with the Array
};
Also how do you do use the parameter "arrayNameInMethod" to replace the object. Basically:
if(Picture Clicked and picture is Unhidden) {
[NSMutableArray *(differentArrayNameInMethod) replaceObjectAtIndex:5 withObject: #"True)
};
(this is all in another method)
Comment #2: You can't use the parameters the same way because it's a string. You can't access an array with a name as a string.
Thank you so much!
I think what you will need is a dictionary, mapping the name to the array. I will give a barebones implementation:
#interface YourClass()
{
NSMutableDictionary *_arrayMap;
}
#end
#interface YourClass
- (instancetype)init
{
self = [super init];
if (self) {
// You might do this somewhere else, like viewDidLoad:
_arrayMap = [NSMutableDictionary new];
}
return self;
}
- (void)someMethod
{
// Some functionality to add a new array:
// Note the array contains NSNumber and NSString objects:
_arrayMap[#"sampleName"] = [#[ #(0), #"one", #(2.0), #"three", #(4), #(YES)] mutableCopy];
}
-(BOOL)checkForConditionInArrayNamed:(NSString *)arrayName
{
BOOL retval = NO;
NSMutableArray *array = _arrayMap[arrayName];
if ([array count] > 5) {
id obj = array[5];
if ([obj isKindOfClass:[NSNumber class]])
retval = [obj boolValue];
}
return retval
};
#end
You then call checkForConditionInArrayNamed: to check for the condition of the named array and act accordingly.

What is the most efficient way to generate a sequence of NSNumbers?

It's a fairly simple builtin in python for example: x = range(0,100)
How can I accomplish the same feat using objective-c methods? Surely there is something better than a NSMutableArray and a for-loop:
NSMutableArray *x = [NSMutableArray arrayWithCapacity:100];
for(int n=0; n<100; n++) {
[x addObject:[NSNumber numberWithInt:n]];
}
Yes, I am aware that doing this is most likely not what I actually want to do (ex: xrange in python), but humor my curiosity please. =)
Clarification: I would like a NSArray containing a sequence of NSNumbers, so that the array could be further processed for example by shuffling elements or sorting by an external metric.
If you want such an array, you might want to do your own specific subclass of NSArray.
A very basic implementation example would look like:
#interface MyRangeArray : NSArray
{
#private
NSRange myRange;
}
+ (id)arrayWithRange:(NSRange)aRange;
- (id)initWithRange:(NSRange)aRange;
#end
#implementation MyRangeArray
+ (id)arrayWithRange:(NSRange)aRange
{
return [[[self alloc] initWithRange:aRange] autorelease];
}
- (id)initWithRange:(NSRange)aRange
{
self = [super init];
if (self) {
// TODO: verify aRange limits here
myRange = aRange;
}
return self;
}
- (NSUInteger)count
{
return myRange.length;
}
- (id)objectAtIndex:(NSUInteger)index
{
// TODO: add range check here
return [NSNumber numberWithInteger:(range.location + index)];
}
#end
After that, you can override some other NSArray methods to make your class more efficient.
NSRange range = NSMakeRange(0, 100);
You can iterate this range by:
NSUInteger loc;
for(loc = range.location; loc < range.length; loc++)
{
}

Custom single KeyValuePair class vs NSMutableDictionary

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];
}

Outputting iVars from description method?

I am pretty sure I am just missing the point here and getting confused. Can anyone tell me how I might write a simple description for an object that will print out the value of its instance variables to the console.
Also: is there anyway to present the information as a block (i.e. if you had 10 iVars its going to be a pain getting them all to return one by one)
#interface CelestialBody : NSObject {
NSString *bodyName;
int bodyMass;
}
- (NSString *)description {
return (#"Name: %# Mass: %d", bodyName, bodyMass);
}
cheers -gary-
- (NSString*)description
{
return [NSString stringWithFormat:#"Name: %#\nMass: %d\nFoo: %#",
bodyName, bodyMass, foo];
}
Look at the answer to this question. The code is reproduced below:
unsigned int varCount;
Ivar *vars = class_copyIvarList([MyClass class], &varCount);
for (int i = 0; i < varCount; i++) {
Ivar var = vars[i];
const char* name = ivar_getName(var);
const char* typeEncoding = ivar_getTypeEncoding(var);
// do what you wish with the name and type here
}
free(vars);
As Jason wrote you should use stringWithFormat: to format strings with printf like syntax.
-(NSString*)description;
{
return [NSString stringWithFormat:#"Name: %# Mass: %d", bodyName, bodyMass];
}
To avoid writing this over and over again for many classes you could add a category on NSObject that allows you to inspect instance variables easily. This will be bad performance, but works for debugging purposes.
#implementation NSObject (IvarDictionary)
-(NSDictionary*)dictionaryWithIvars;
{
NSMutableDictionary* dict = [NSMutableDictionary dictionary];
unsigned int ivarCount;
Ivar* ivars = class_copyIvarList([self class], &ivarCount);
for (int i = 0; i < ivarCount; i++) {
NSString* name = [NSString stringWithCString:ivar_getName(ivars[i])
encoding:NSASCIIStringEncoding];
id value = [self valueForKey:name];
if (value == nil) {
value = [NSNull null];
}
[dict setObject:value forKey:name];
}
free(vars);
return [[dict copy] autorelease];
}
#end
With this in place implementing description is also a piece of cake:
-(NSString*)description;
{
return [[self dictionaryWithIvars] description];
}
Do not add this description as a category on NSObject, or you might end up with infinite recursions.
That's not a bad idea what you had there, it's almost achievable too.
// choose a short name for the macro
#define _f(x,...) [NSString stringWithFormat:x,__VA_ARGS__]
...
- (NSString *) description
{
return _f(#"Name: %# Mass: %d", bodyName, bodyMass);
}

Sample Cocoa app, ref counting, [pool drain] yields EXC_BAD_ACCESS?

I'm working through Cocoa Programming for Mac OS X (3rd ed) and in chapter 4 I wrote this app:
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
//create the date object
NSCalendarDate *now = [[NSCalendarDate alloc] init];
//seed random # generator
srandom(time(NULL));
NSMutableArray *array;
array = [[NSMutableArray alloc] init];
int i;
for (i=0; i<10; i++){
//create a date/time that is 'i' weeks from now
NSCalendarDate *iWeeksFromNow;
iWeeksFromNow = [now dateByAddingYears:0
months:0
days:(i*7)
hours:0
minutes:0
seconds:0];
//create a new instance of lottery entry
LotteryEntry *entry = [[LotteryEntry alloc] init];
[entry setEntryDate:iWeeksFromNow];
[array addObject:entry];
[entry release];
}
[now release];
now = nil;
for (LotteryEntry *entryToPrint in array) {
NSLog(#"%#", entryToPrint);
}
[array release];
array = nil;
NSLog(#"about to drain the pool... (%#)", pool);
[pool drain];
NSLog(#"done");
NSLog(#"GC = %#", [NSGarbageCollector defaultCollector]);
return 0;
}
The LotteryEntry class looks like this:
#implementation LotteryEntry
- (void)setEntryDate:(NSCalendarDate *)date
{
entryDate = date;
}
- (NSCalendarDate *)entryDate
{
return entryDate;
}
- (int)firstNumber
{
return firstNumber;
}
- (int)secondNumber
{
return secondNumber;
}
- (id)init
{
return [self initWithDate:[NSCalendarDate calendarDate]];
}
- (id)initWithDate:(NSCalendarDate *)date
{
if(![super init])
return nil;
NSAssert(date != nil, #"Argument must be non-nil");
firstNumber = random() % 100 + 1;
secondNumber = random() % 100 + 1;
entryDate = [date retain];
return self;
}
- (NSString *)description
{
NSString *result;
result = [[NSString alloc] initWithFormat:#"%# = %d and %d",
[entryDate descriptionWithCalendarFormat:#"%b %d %Y"],
firstNumber,
secondNumber];
return result;
}
- (void)dealloc
{
NSLog(#"deallocating %#", self);
[entryDate release];
[super dealloc];
}
#end
As you can see I'm retaining and releasing the objects here. I'm pretty sure my code matches the book's, however when I run the app, at the [pool drain] I get this message:
Program received signal:
“EXC_BAD_ACCESS”.
I'm not sure what's causing this. I'm hoping it's something stupid that I missed, but I'd sure appreciate a few other pairs of eyes on it. Thanks in advance!
(side note: I'm a .NET developer, so ref counting is pretty foreign to me!)
It also looks like you have a bug in this method:
- (id)initWithDate:(NSCalendarDate *)date
{
if(![super init])
return nil;
NSAssert(date != nil, #"Argument must be non-nil");
firstNumber = random() % 100 + 1;
secondNumber = random() % 100 + 1;
entryDate = [date retain];
return self;
}
You are essentially discarding the results from [super init], while it may not be a problem in this instance, it could cause serious problems in others. You should 'always' structure you init methods like this:
- (id)initWithDate:(NSCalendarDate *)date
{
if(self = [super init]) {
NSAssert(date != nil, #"Argument must be non-nil");
firstNumber = random() % 100 + 1;
secondNumber = random() % 100 + 1;
entryDate = [date retain];
}
return self;
If you are not going to return self from an init method (for instance it is a factory or something odd like that), you should remember to release self. It has been alloc'ed, and if you don't return it, it cannot be released properly. Example:
- (id) init
{
NSObject* newSelf = [[NSObject alloc] init];
[self release];
return newSelf;
}
doh! Just typing the code made me realize my problem. Doncha love it when that happens?
I am retaining the date in my init, but I still had that extra setEntryDate method that was not calling retain. Removing this and calling the initWithDate method instead seemed to fix the problem.
It's not related to your issue at hand but you should avoid using NSCalendarDate, it's been deprecated for a while now and will probably be removed from the API entirely soon.