Usually I treat instance variables in Objective-c like this:
#interface MyClass : NSObject
#property (nonatomic, retain) NSMutableArray *mutableArray;
#end
#implementation MyClass
#synthesize mutableArray;
- (id) init {
if((self = [super init])) {
self.mutableArray = [NSMutableArray array];
}
return self;
}
- (void) dealloc {
[mutableArray release];
[super dealloc];
}
#end
I feel pretty comfortable w/ the above syntax. However I'm not so comfortable w/ the syntax for a 2D array instance variable like NSUInteger 2dArray[10][10].
What's the appropriate Objective-c syntax for a 2d array instance variable with regards to interface declaration, synthesizing getters/setters and memory management?
You don't need to allocate memory for your array; they are perfectly fine being defined in the class and they will always exist, at the same size. You therefore don't need to worry about memory management and your getter/setters should be defined manually, depending on what you want to do. For example these getter/setter methods allow getting/setting an individual value:
#interface MyClass : NSObject
{
NSUInteger _twoDeeArray[10][10];
}
- (void)setTwoDeeArrayX:(NSUInteger)x y:(NSUInteger)y value:(NSUInteger)value;
- (NSUInteger)twoDeeArrayX:(NSUInteger)x y:(NSUInteger)y;
#end
#implementation MyClass
- (void)setTwoDeeArrayX:(NSUInteger)x y:(NSUInteger)y value:(NSUInteger)value
{
_twoDeeArray[x][y] = value;
}
- (NSUInteger)twoDeeArrayX:(NSUInteger)x y:(NSUInteger)y
{
return _twoDeeArray[x][y];
}
#end
You should probably have range-checking for x and y, but you get the idea.
That's not an Objective C syntax. It's pure C syntax. You don't need to exclusively say that you want a 2D array of objc objects. Just declare/define the mutable array and add other arrays to it.
For two-demensional arrays, you can:
Go with C arrays (like what you mentioned in the post)
Add NSMutableArray into NSMutableArray
Create a class to implement your version of 2D-array
If you just want to use primitive types in your array, all three are good.
For Objective-C objects, you can also go with C array with id type but you have to manage memory allocation/deallocation yourself. 2 and 3 are better way to do this.
FYI:
2D arrays using NSMutableArray
Creating a two dimensional array in Objective-C
in iOS 6 you can use subscript to define a matrix class that uses the square bracket syntax matrix[row][col] where you can store objects and they are correctly retained by the matrix, differently than using a C array
First create a Row object, defined like this
- (id)initWithElementNumber:(NSUInteger)num {
if (self = [super init]) {
_row = [NSMutableArray arrayWithCapacity:num];
for (int j = 0; j < num; j++)
[_row addObject:[NSNull null]];
}
return self;
}
- (id)objectAtIndexedSubscript:(NSUInteger)idx {
return self.row[idx];
}
- (void)setObject:(id)object atIndexedSubscript:(NSUInteger)idx {
self.row[idx] = object;
}
#end
And then a Matrix class that uses the Row class previously defined:
#implementation UKMatrix
- (id)initWithRows:(NSUInteger)numRows columsn:(NSUInteger)numCol {
if (self = [super init])
{
_numCol = numCol;
_numRows = numRows;
_rows = [NSMutableArray arrayWithCapacity:numRows];
for (int j = 0; j < numRows; j++)
[_rows addObject:[[UKRow alloc] initWithElementNumber:numCol]];
}
return self;
}
- (id)objectAtIndexedSubscript:(NSUInteger)idx {
return self.rows[idx];
}
- (NSString *)description {
NSString *matrixDesc = #"";
for (int j = 0; j < self.numRows; j++) {
matrixDesc = [matrixDesc stringByAppendingString:#"\n"];
for (int k = 0; k < self.numCol; k++)
matrixDesc = [matrixDesc stringByAppendingFormat:#" %# ",self[j][k]];
}
return matrixDesc;
}
#end
then you can use the Matrix with the following syntax
UKMatrix *matrix = [[UKMatrix alloc] initWithRows:4 columsn:2];
matrix[1][1] = #2;
NSLog(#"%#", matrix);
Related
I'm currently working on a project that's split in two classes, the class 'Array' and the class 'PPCalcVals'. Because other classes, that will be added, will also have to have access the array, I thought it would be the best to write the array class, containing an NSMutableArray and Subclass all the others (beginning with the PPCalcVals class.
So the 'PPCalcVals' class needs to access the elements of the array in the superclass 'Array'.
(Please correct me if this is the wrong approach).
As mentioned the whole program is written and well working in C but to create a GUI and eventually an OSX or IOS application I started to learn OOProgramming with Objecitve C.
Anyways, when I reference the objects in the array of the superclass the only value that gets printed is "null" which is not really what I want.
Here is the code:
main routine:
#import <Foundation/Foundation.h>
#import "Array.h"
#import "PPCalcVals.h"
int main(int argc, const char * argv[])
{
#autoreleasepool
{
Array *prices = [[Array alloc]initWithName:#0];
PPCalcVals *myVals = [[PPCalcVals alloc]init];
[prices addValue:#12];
[prices addValue:#13];
[prices addValue:#14];
[prices addValue:#15];
[prices addValue:#15];
[prices print];
[myVals print];
}
return 0;
}
array.h file:
#import <Foundation/Foundation.h>
#interface Array : NSObject
{
NSMutableArray *prices;
}
-(id) initWithName: (NSNumber *) values;
-(void) addValue: (NSNumber *) value;
-(void) print;
-(NSMutableArray *) prices;
#end
array.m
#import "Array.h"
#implementation Array
-(id) initWithName:(NSNumber *)values
{
self = [super init];
if(self)
{
prices = [NSMutableArray array];
}
return self;
}
-(void) addValue: (NSNumber *) value
{
[prices addObject:value];
}
-(void) print
{
NSLog(#"%#",prices);
}
-(NSMutableArray *)prices
{
return prices;
}
#end
PPCalcVals.h:
#import "Array.h"
#interface PPCalcVals : Array
#property id high,low,open,close;
-(void) setHigh:(NSMutableArray *)h setLow:(NSMutableArray *)l; //set high and low
-(void) setOpen:(NSMutableArray *)o setClose:(NSMutableArray *)c; //set open and close
-(void) sort; //sort array
-(void) print; //debugging tool
#end
PPCalcVals.m:
#import "PPCalcVals.h"
#implementation PPCalcVals
#synthesize high,low,open,close;
-(void) setOpen:(NSMutableArray *)o setClose:(NSMutableArray *)c
{
o = prices[0];
c = prices[2];
open = o;
close = c;
}
-(void) sort;
{
[prices sortedArrayUsingComparator:^(NSString *str1, NSString *str2) {
return [str1 compare:str2 options:NSNumericSearch];
}];
}
-(void) setHigh:(NSMutableArray *)h setLow:(NSMutableArray *)
{
h = prices[0];
l = prices[2];
high = h;
low = l;
}
-(void) print
{
NSLog(#"open: %#",open);
NSLog(#"close: %#",close);
NSLog(#"high: %#",high);
NSLog(#"low: %#",low);
}
#end
The running program outputs only:
2013-08-05 10:21:08.546 prog1[1314:303] (
12,
13,
14,
15,
15
)
open: (null)
close: (null)
high: (null)
low: (null)
I realize that this is probably a really basic question but I would appreciate your help a lot and I already thank you if you read until this point ;)
You have created two distinct, completely unrelated objects prices and myVals:
Array *prices = [[Array alloc]initWithName:#0];
PPCalcVals *myVals = [[PPCalcVals alloc]init];
What you probably meant is to create a PPCalcVals object (which inherits
all methods from the superclass Array):
PPCalcVals *prices = [[PPCalcVals alloc]init];
[prices addValue:#12]; // invokes `addValue` method from superclass "Array"
// ...
[prices print]; // invokes `print` method from "PPCalcVals"
You also have to implement init in the PPCalcVals class in such a way
that it calls the "designated initializer" initWithName in the superclass.
I have one question. Where did you make calls for the functions
-(void) setHigh:(NSMutableArray *)h setLow:(NSMutableArray *)l;
-(void) setOpen:(NSMutableArray *)o setClose:(NSMutableArray *)c;
Because all I can see is that these functions are not called. So the properties high, low, open & close are not yet assigned and thus they return Null.
Also may I know the reason why are you overriding the parameters inside above mentioned functions?
o = prices[0];
c = prices[2];
and
h = prices[0];
l = prices[2];
Noob question here.
If I have Class A with an array float itemsPosition[20][20] and I have another class B to access that, how can I do it?
What I usually do it to alloc Class A and access for other objects but in this case, I can't synthesize float array within Class A.
Any ideas?
You can #synthesize an NSValue that holds a pointer to your array.
#interface SomeObject : NSObject
#property (strong, nonatomic) NSValue *itemsPosition;
#end
#implementation SomeObject
#synthesize itemsPosition;
...
static float anArray[20][20];
...
- (void) someMethod
{
... add items to the array
[self setItemsPosition:[NSValue valueWithPointer:anArray]];
}
#end
#implementation SomeOtherObject
...
- (void) someOtherMethod
{
SomeObject *obj = [[SomeObject alloc] init];
...
float (*ary2)[20] = (float(*)[20])[obj.itemsPosition pointerValue];
...
}
Floats are C-types and so you can't use the typical Objective C property thing to access them directly.
Best thing to do is create an "accessor" function that gives class B access to the pointer for the very first array entry "itemsPosition". E.G. "itemsPosition[0][0]"
In class A's .h file:
float itemsPosition[20][20];
- (float *) getItemsPosition;
and in the .m file:
- (float *) getItemsPosition
{
// return the location of the first item in the itemsPosition
// multidimensional array, a.k.a. itemsPosition[0][0]
return( &itemsPosition[0][0] );
}
And in class B, since you know the size of this multidimensional array is 20 x 20, you can step to the location of the next array entry pretty easily:
float * itemsPosition = [classA getItemsPosition];
for(int index = 0; index < 20; index++)
{
// this takes us to to the start of itemPosition[index]
float * itemsPositionAIndex = itemsPosition+(index*20);
for( int index2 = 0; index2 < 20; index2++)
{
float aFloat = *(itemsPositionAIndex+index2);
NSLog( #"float %d + %d is %4.2f", index, index2, aFloat);
}
}
}
Let me know if it would be useful for me to put a sample Xcode project up for you somewhere.
Is there a way to force NSMutableArray to hold one specific object type only?
I have classes definitions as follow:
#interface Wheel:NSObject
{
int size;
float diameter;
}
#end
#interface Car:NSObject
{
NSString *model;
NSString *make;
NSMutableArray *wheels;
}
#end
How can I force wheels array to hold Wheel objects only with code? (and absolutely not other objects)
Update in 2015
This answer was first written in early 2011 and began:
What we really want is parametric polymorphism so you could declare, say, NSMutableArray<NSString>; but alas such is not available.
In 2015 Apple apparently changed this with the introduction of "lightweight generics" into Objective-C and now you can declare:
NSMutableArray<NSString *> *onlyStrings = [NSMutableArray new];
But all is not quite what it seems, notice the "lightweight"... Then notice that the initialisation part of the above declaration does not contain any generic notation. While Apple have introduced parametric collections, and adding a non-string directly to the above array, onlyStrings, as in say:
[onlyStrings addObject:#666]; // <- Warning: Incompatible pointer types...
will illicit the warning as indicated, the type security is barely skin deep. Consider the method:
- (void) push:(id)obj onto:(NSMutableArray *)array
{
[array addObject:obj];
}
and the code fragment in another method of the same class:
NSMutableArray<NSString *> *oops = [NSMutableArray new];
[self push:#"asda" onto:oops]; // add a string, fine
[self push:#42 onto:oops]; // add a number, no warnings...
What Apple have implemented is essentially a hinting system to assist with automatic inter-operation with Swift, which does have a flavour of type-safe generics. However on the Objective-C side, while the compiler provides some extra hints the system is "lightweight" and type-integrity is still ultimately down to the programmer - as is the Objective-C way.
So which should you use? The new lightweight/pseudo generics, or devise your own patterns for your code? There really is no right answer, figure out what makes sense in your scenario and use it.
For example: If you are targeting interoperation with Swift you should use the lightweight generics! However if the type integrity of a collection is important in your scenario then you could combine the lightweight generics with your own code on the Objective-C side which enforces the type integrity that Swift will on its side.
The Remainder of the 2011 Answer
As another option here is a quick general subclass of NSMutableArray which you init with the kind of object you want in your monomorphic array. This option does not give you static type-checking (in as much as you ever get it in Obj-C), you get runtime exceptions on inserting the wrong type, just as you get runtime exceptions for index out of bounds etc.
This is not thoroughly tested and assumes the documentation on overriding NSMutableArray is correct...
#interface MonomorphicArray : NSMutableArray
{
Class elementClass;
NSMutableArray *realArray;
}
- (id) initWithClass:(Class)element andCapacity:(NSUInteger)numItems;
- (id) initWithClass:(Class)element;
#end
And the implementation:
#implementation MonomorphicArray
- (id) initWithClass:(Class)element andCapacity:(NSUInteger)numItems
{
elementClass = element;
realArray = [NSMutableArray arrayWithCapacity:numItems];
return self;
}
- (id) initWithClass:(Class)element
{
elementClass = element;
realArray = [NSMutableArray new];
return self;
}
// override primitive NSMutableArray methods and enforce monomorphism
- (void) insertObject:(id)anObject atIndex:(NSUInteger)index
{
if ([anObject isKindOfClass:elementClass]) // allows subclasses, use isMemeberOfClass for exact match
{
[realArray insertObject:anObject atIndex:index];
}
else
{
NSException* myException = [NSException
exceptionWithName:#"InvalidAddObject"
reason:#"Added object has wrong type"
userInfo:nil];
#throw myException;
}
}
- (void) removeObjectAtIndex:(NSUInteger)index
{
[realArray removeObjectAtIndex:index];
}
// override primitive NSArray methods
- (NSUInteger) count
{
return [realArray count];
}
- (id) objectAtIndex:(NSUInteger)index
{
return [realArray objectAtIndex:index];
}
// block all the other init's (some could be supported)
static id NotSupported()
{
NSException* myException = [NSException
exceptionWithName:#"InvalidInitializer"
reason:#"Only initWithClass: and initWithClass:andCapacity: supported"
userInfo:nil];
#throw myException;
}
- (id)initWithArray:(NSArray *)anArray { return NotSupported(); }
- (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag { return NotSupported(); }
- (id)initWithContentsOfFile:(NSString *)aPath { return NotSupported(); }
- (id)initWithContentsOfURL:(NSURL *)aURL { return NotSupported(); }
- (id)initWithObjects:(id)firstObj, ... { return NotSupported(); }
- (id)initWithObjects:(const id *)objects count:(NSUInteger)count { return NotSupported(); }
#end
Use as:
MonomorphicArray *monoString = [[MonomorphicArray alloc] initWithClass:[NSString class] andCapacity:3];
[monoString addObject:#"A string"];
[monoString addObject:[NSNumber numberWithInt:42]]; // will throw
[monoString addObject:#"Another string"];
Since Xcode 7, generics are available in Objective-C.
You can declare a NSMutableArray as:
NSMutableArray <Wheel*> *wheels = [[NSMutableArray alloc] initWithArray:#[[Wheel new],[Wheel new]];
The compiler will give you a warning if you try to put non-Wheel objects in the array.
I could be wrong (I'm a noob), but I think, if you create a custom protocol and make sure the objects you are adding to the array follow the same protocol, then when you declare the array you use
NSArray<Protocol Name>
That should prevent objects being added that do not follow the said protocol.
as per i know.. before you added any object in wheels mutableArray, u have to add some check mark. Is the object which i am adding is class "wheel". if it is then add, other wise not.
Example:
if([id isClassOf:"Wheel"] == YES)
{
[array addObject:id)
}
Something like this. i dont remember the exact syntax.
I hope this will help (and work... :P )
Wheel.h file:
#protocol Wheel
#end
#interface Wheel : NSObject
#property ...
#end
Car.h file:
#import "Wheel.h"
#interface Car:NSObject
{
NSString *model;
NSString *make;
NSMutableArray<Wheel, Optional> *wheels;
}
#end
Car.m file:
#import "Car.h"
#implementation Car
-(id)init{
if (self=[super init]){
self.wheels = (NSMutableArray<Wheel,Optional>*)[NSMutableArray alloc]init];
}
return self;
}
#end
Xcode 7 allows you to define Arrays, Dictionaries, and even your own Classes as having generics. The array syntax is as follows:
NSArray<NSString*>* array = #[#"hello world"];
I don't believe there's any way to do it with NSMutableArray out of the box. You could probably enforce this by subclassing and overriding all the constructors and insertion methods, but it's probably not worth it. What are you hoping to achieve with this?
That's not possible; an NSArray (whether mutable or not) will hold any object type. What you can do is to create your own custom subclasses as already suggested by Jim. Alternatively, if you wanted to filter an array to remove objects that weren't of the type you want, then you could do:
- (void)removeObjectsFromArray:(NSMutableArray *)array otherThanOfType:(Class)type
{
int c = 0;
while(c < [array length])
{
NSObject *object = [array objectAtIndex:c];
if([object isKindOfClass:type])
c++;
else
[array removeObjectAtIndex:c];
}
}
...
[self removeObjectsFromArray:array otherThanOfType:[Car class]];
Or make other judgments based on the result of isKindOfClass:, e.g. to divide an array containing a mixture of Cars and Wheels into two arrays, each containing only one kind of object.
You can use the nsexception if you dont have the specific object.
for (int i = 0; i<items.count;i++) {
if([[items objectAtIndex:i] isKindOfClass:[Wheel class]])
{
// do something..!
}else{
[NSException raise:#"Invalid value" format:#"Format of %# is invalid", items];
// do whatever to handle or raise your exception.
}
}
Here's something I've done to avoid subclassing NSMutableArray: use a category. This way you can have the argument and return types you want. Note the naming convention: replace the word "object" in each of the methods you will use with the name of the element class. "objectAtIndex" becomes "wheelAtIndex" and so on. This way there's no name conflict. Very tidy.
typedef NSMutableArray WheelList;
#interface NSMutableArray (WheelList)
- (wheel *) wheelAtIndex: (NSUInteger) index;
- (void) addWheel: (wheel *) w;
#end
#implementation NSMutableArray (WheelList)
- (wheel *) wheelAtIndex: (NSUInteger) index
{
return (wheel *) [self objectAtIndex: index];
}
- (void) addWheel: (wheel *) w
{
[self addObject: w];
}
#end
#interface Car : NSObject
#property WheelList *wheels;
#end;
#implementation Car
#synthesize wheels;
- (id) init
{
if (self = [super init]) {
wheels = [[WheelList alloc] initWithCapacity: 4];
}
return self;
}
#end
protocol maybe a good idea:
#protocol Person <NSObject>
#end
#interface Person : NSObject <Person>
#end
to use:
NSArray<Person>* personArray;
There is one-header file project which allows this:
Objective-C-Generics
Usage:
Copy ObjectiveCGenerics.h to your project.
When defining a new class use the GENERICSABLE macro.
#import "ObjectiveCGenerics.h"
GENERICSABLE(MyClass)
#interface MyClass : NSObject<MyClass>
#property (nonatomic, strong) NSString* name;
#end
Now you can use generics with arrays and sets just as you normally do in Java, C#, etc.
Code:
I have a class that inherits from NSObject. It uses an NSMutableArray to hold children objects, e.g. People using NSMutableArray *items to hold Person objects. How do I implement the NSFastEnumerator on items?
I have tried the following but it is invalid:
#interface People : NSObject <NSFastEnumeration>
{
NSMutableArray *items;
}
#implementation ...
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len
{
if(state->state == 0)
{
state->mutationsPtr = (unsigned long *)self;
state->itemsPtr = items;
state->state = [items count];
return count;
}
else
return 0;
}
You are not using the NSFastEnumerationState structure properly. See NSFastEnumeration Protocol Reference and look at the constants section to see a description of each of the fields. In your case, you should leave state->mutationsPtr as nil. state->itemsPtr should be set to a C-array of the objects, not an NSArray or NSMutableArray. You also need to put the same objects into the array passed as stackbuf.
However, since you are using an NSMutableArray to contain the objects you are enumerating, you could just forward the call to that object:
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len {
return [items countByEnumeratingWithState:state objects:stackbuf count:len];
}
There's an NSFastEnumeration protocol, but you are using the (non-existent) NSFastEnumerator protocol. Could that be the problem?
I know that if you have a loop that modifies the count of the items in the loop, using the NSEnumerator on a set is the best way to make sure your code blows up, however I would like to understand the performance tradeoffs between the NSEnumerator class and just an old school for loop
Using the new for (... in ...) syntax in Objective-C 2.0 is generally the fastest way to iterate over a collection because it can maintain a buffer on the stack and get batches of items into it.
Using NSEnumerator is generally the slowest way because it often copies the collection being iterated; for immutable collections this can be cheap (equivalent to -retain) but for mutable collections it can cause an immutable copy to be created.
Doing your own iteration — for example, using -[NSArray objectAtIndex:] — will generally fall somewhere in between because while you won't have the potential copying overhead, you also won't be getting batches of objects from the underlying collection.
(PS - This question should be tagged as Objective-C, not C, since NSEnumerator is a Cocoa class and the new for (... in ...) syntax is specific to Objective-C.)
After running the test several times, the result is almost the same. Each measure block runs 10 times consecutively.
The result in my case from the fastest to the slowest:
For..in (testPerformanceExample3) (0.006 sec)
While (testPerformanceExample4) (0.026 sec)
For(;;) (testPerformanceExample1) (0.027 sec)
Enumeration block (testPerformanceExample2) (0.067 sec)
The for and while loop is almost the same.
The tmp is an NSArray which contains 1 million objects from 0 to 999999.
- (NSArray *)createArray
{
self.tmpArray = [NSMutableArray array];
for (int i = 0; i < 1000000; i++)
{
[self.tmpArray addObject:#(i)];
}
return self.tmpArray;
}
The whole code:
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (strong, nonatomic) NSMutableArray *tmpArray;
- (NSArray *)createArray;
#end
ViewController.m
#import "ViewController.h"
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self createArray];
}
- (NSArray *)createArray
{
self.tmpArray = [NSMutableArray array];
for (int i = 0; i < 1000000; i++)
{
[self.tmpArray addObject:#(i)];
}
return self.tmpArray;
}
#end
MyTestfile.m
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import "ViewController.h"
#interface TestCaseXcodeTests : XCTestCase
{
ViewController *vc;
NSArray *tmp;
}
#end
#implementation TestCaseXcodeTests
- (void)setUp {
[super setUp];
vc = [[ViewController alloc] init];
tmp = vc.createArray;
}
- (void)testPerformanceExample1
{
[self measureBlock:^{
for (int i = 0; i < [tmp count]; i++)
{
[tmp objectAtIndex:i];
}
}];
}
- (void)testPerformanceExample2
{
[self measureBlock:^{
[tmp enumerateObjectsUsingBlock:^(NSNumber *obj, NSUInteger idx, BOOL *stop) {
obj;
}];
}];
}
- (void)testPerformanceExample3
{
[self measureBlock:^{
for (NSNumber *num in tmp)
{
num;
}
}];
}
- (void)testPerformanceExample4
{
[self measureBlock:^{
int i = 0;
while (i < [tmp count])
{
[tmp objectAtIndex:i];
i++;
}
}];
}
#end
For more information visit: Apples "About Testing with Xcode"
They are very similar. With Objective-C 2.0 most enumerations now default to NSFastEnumeration which creates a buffer of the addresses to each object in the collection that it can then deliver. The one step that you save over the classic for loop is not having to call objectAtIndex:i each time inside the loop. The internals of the collection you are enumerating implement fast enumeration with out calling objectAtIndex:i method.
The buffer is part of the reason that you can't mutate a collection as you enumerate, the address of the objects will change and the buffer that was built will no longer match.
As a bonus the format in 2.0 looks as nice as the classic for loop:
for ( Type newVariable in expression ) {
stmts
}
Read the following documentaion to go deeper:
NSFastEnumeration Protocol Reference