Reputation: 55
I am trying to create an NSMutableArray
to hold some objects but I m having real trouble understanding the proper syntax for creating one that all my methods in main.m
can add to and access.
I have tried making it an @property
in the interface of the object i want the array to store and i have also tried creating a custom init method that would initialise one, but outside of the method that creates the object and adds it to the array I am getting undeclared identifier errors on the array name.
I am more used to java where I can create the array that all methods can access but I understand that Objective-C
would not allow this. Can someone give me a hand and give me an example of code to create an NSMutableArray
that all my methods in main.m
can access?
Upvotes: 1
Views: 2372
Reputation: 62052
DISCLAIMER: The fact that you are writing code in main.m
suggests that you've start a console app project in Xcode and aren't trying to write a Cocoa or Cocoa Touch app.
If you are trying to write a Cocoa or Cocoa Touch application for OS X or iOS, your main.m
file should look about like this:
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
And you should know what you're doing before you modify it (99.999% of the time, you'll never need to touch this file for a Cocoa/Cocoa Touch app).
So to restate, this answer assumes you're writing a simply console app to teach your self some Objective-C basics.
And if this assumption is incorrect, your question should be closed as unclear what you're asking.
Objective-C is a strict super-set of C. As such, anything we can do in C, we can also do in Objective-C. This includes declaring variables at the global scope. This is different from Java. In Java, everything is an object--everything resides in a class file. This includes the main
method. Example:
public class MyApplication
{
public static Object[] myArray;
public static void main(String [] args)
{
// execution begins here
}
}
This is different from Objective-C. Not everything must reside in a class in Objective-C. If we want a global variable in Objective-C, we just declare it in the global scope. Example:
#import <Foundation/Foundation.h>
NSMutableArray * myArray;
int main(int argc, const char * argv[]) {
@autoreleasepool {
myArray = [NSMutableArray array];
}
return 0;
}
We can use myArray
anywhere within main.m
when we've declared it as such.
Let's note something here though... if it's not a member of a class, it's called a function, not a method. So we can declare C-style functions also in this global scope.
NSMutableArray * myArray;
void appendHelloWorld() {
[myArray addObject:@"Hello World"];
}
NSInteger myArrayCount() {
return myArray.count;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
myArray = [NSMutableArray array];
while (myArrayCount() < 10) {
appendHelloWorld();
}
NSLog(@"%@", myArray);
}
return 0;
}
This all works perfectly fine. If we run this program, we'll get the following output:
2015-05-01 13:22:21.188 ObjC[18340:4210463] (
"Hello World",
"Hello World",
"Hello World",
"Hello World",
"Hello World",
"Hello World",
"Hello World",
"Hello World",
"Hello World",
"Hello World"
)
I've merely demonstrated to you exactly how to do exactly what your asking for, but we really don't want to get into doing things this way. This is the path to using globals as the answer to everything. This is the path to using singletons as the answer to everything. This is the path to writing applications that have 12 different singleton classes (I've seen it). This isn't the path to Sparta--this is the path to madness.
Instead, we should be dealing with instances of objects.
Our functions (or when we graduate to classes, our methods) should be passed an argument, and we shouldn't have a variable in the global scope.
So, let's get rid of the global variable and amend our methods:
void appendHelloWorld(NSMutableArray *array) {
[array addObject:@"Hello World"];
}
NSInteger arrayCount(NSArray *array) {
return array.count;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableArray *myArray = [NSMutableArray array];
while (arrayCount(myArray) < 10) {
appendHelloWorld(myArray);
}
NSLog(@"%@", myArray);
}
return 0;
}
Running this program gives us identical output:
2015-05-01 13:27:48.594 ObjC[18361:4213356] (
"Hello World",
"Hello World",
"Hello World",
"Hello World",
"Hello World",
"Hello World",
"Hello World",
"Hello World",
"Hello World",
"Hello World"
)
It also makes our code more flexible. Our function, appendHelloWorld
isn't so tightly coupled to this code-based. It can be plunked out of this and into anywhere else and perform exactly as expected. It's not coupled to anything. It will drop the string, @"Hello World"
, onto any mutable array it is passed. And while arrayCount()
is really unnecessary (just using it as an example), we can say all of the same things for it.
We don't want to use global variables and we don't want functions or methods so tightly coupled to global variables. We need to get comfortable with the idea of instantiating instances of objects and passing these objects to the methods or functions that need them.
Singletons should be used not when it is important for multiple classes to access some shared memory store--NO--that's not what singletons are for. Singletons should be used when you need to be certain only one instance of the class is instantiated ever in the life time of the app.
Upvotes: 7
Reputation: 808
Not that I really understand why you would want to have a lot of functions in main.m. One way to achieve what you say you want to do, but probably don't want to do, is to create a class with a static instance
@interface GlobalArrayHolder
@property (nonatomic, readonly) NSMutableArray *globalArray;
+ (instancetype)sharedGlobalArrayHolder;
@end
With an implementation something like
@implementation GlobalArrayHolder
@synthesize globalArray = _globalArray
- (instancetype) init {
self = [super init];
if (self) {
_globalArray = [NSMutableArray arrayWithCapacity:5];
}
return self;
}
+ (instancetype) sharedGlobalArrayHolder {
static GlobalArrayHolder *localGlobalArrayHolder = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
localGlobalArrayHolder = [[GlobalArrayHolder alloc] init];
});
return localGlobalArrayHolder;
}
@end
You want to read up about how an iOS or OS X app actually works and learn about the app delegate. Adding a lot of functions to main.m sounds like a really bad idea.
If @nhgrif is correct and you only want to start playing around with Objective-C you could declare the array as static in the file scope of main.m
#import <Foundation/Foundation.h>
static NSMutableArray *globalArray = nil;
int main(int argc, const char *argv[])
{
globalArray = [NSMutableArray arrayWithCapacity:5];
return 0;
}
Please note that this definitely not the way to go about writing a iOS or OS X app. The only thing main should do is to call UIApplicationMain
or NSApplicationMain
, probably inside @autoreleasepool { }
. The XXApplicationMain
method then goes about setting things up before calling out to the app delegate where you start implementing the real work.
To compile with clang from the command line: clang main.m -framework Foundation
Upvotes: 1
Reputation: 2942
I think this is a beginners question. However, MutableArrays can sometime be tricky.
First thing to mention is: NSMutableArrays are NOT thread safe. Keep this in mind if you store them as a property.
If you want something which is accessible from ALL methods, you have a lot of choices. This depends on your desired software architecture.
The most simple thing for global accessible data is NSUserDefaults (for values like preferences, which are stored not very frequently).
If you need to work on that data, you can simply create a so called singleton. A singleton is a class-instance which exists only once. This class would behave like your java example. If you access that class all values are everywhere the same. In general it's not a good advice to use java style development with objC. There is always another option to write good code. To transmit data from an class to another you could use NSNotifications. Another good thing is using delegates (search for delegates and @protocol).
In your special case the following code should work. We call it a singleton. It's a class which has only one instance and is accessible from everywhere:
.h file:
#import <Foundation/Foundation.h>
@interface MySingletonClass : NSObject
@property(nonatomic, strong) NSMutableArray *list;
+(MySingletonClass*) sharedInstance;
@end
.m file:
#import "MySingletonClass.h"
@implementation MySingletonClass
+(MySingletonClass*) sharedInstance {
static MySingletonClass* theInstance = nil;
if (!theInstance) {
theInstance = [[MySingletonClass alloc] init];
// The following line initializes an empty array
theInstance.list = [NSMutableArray array];
}
return theInstance;
}
@end
Now you can import this class everywhere
#import "MySingletonClass.h"
And use it like this:
[[MySingletonClass sharedInstance].list addObject:@"Example object"];
If you are used to 'java' yo maybe switch over to swift. The syntax is more similar to java.
Upvotes: 0