Objective-C 创建和访问可变数组

Objective-C creating and accessing mutable array

我正在尝试创建一个 NSMutableArray 来保存一些对象,但我很难理解创建一个我在 main.m 中的所有方法都可以添加和访问的对象的正确语法。

我已经尝试在我希望数组存储的对象的接口中将其设为 @property,我还尝试创建一个自定义 init 方法来初始化一个,但在创建方法之外对象并将其添加到数组我在数组名称上收到未声明的标识符错误。

我更习惯 java 可以创建所有方法都可以访问的数组,但我知道 Objective-C 不允许这样做。有人可以帮我一个代码示例来创建一个 main.m 中我的所有方法都可以访问的 NSMutableArray 吗?

并不是说我真的理解你为什么想要在 main.m 中有很多功能。实现你说你想做但可能不想做的事情的一种方法是创建一个 class 和一个静态实例

@interface GlobalArrayHolder

@property (nonatomic, readonly) NSMutableArray *globalArray;

+ (instancetype)sharedGlobalArrayHolder;

@end

实现类似

@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

您想了解 iOS 或 OS X 应用程序的实际工作原理并了解应用程序委托。向 main.m 添加大量功能听起来是个糟糕的主意。

如果@nhgrif 是正确的并且您只想开始使用 Objective-C 您可以在 main.m

的文件范围内将数组声明为静态
#import <Foundation/Foundation.h>

static NSMutableArray *globalArray = nil;

int main(int argc, const char *argv[])
{
   globalArray = [NSMutableArray arrayWithCapacity:5];

   return 0;
}

请注意,这绝对不是编写 iOS 或 OS X 应用程序的方法。 main 唯一应该做的就是调用 UIApplicationMainNSApplicationMain,可能在 @autoreleasepool { } 内。 XXApplicationMain 方法然后在调用应用委托之前进行设置,然后您开始实施实际工作。

从命令行用 clang 编译:clang main.m -framework Foundation

我认为这是一个初学者问题。然而,MutableArrays 有时会很棘手。

首先要提到的是:NSMutableArrays 不是线程安全的。如果将它们存储为 属性.

,请记住这一点

如果您想要可以通过所有方法访问的内容,您有很多选择。这取决于您所需的软件架构。

最简单的全局可访问数据是 NSUserDefaults(对于偏好等值,存储不频繁)。

如果您需要处理该数据,您可以简单地创建一个所谓的单例。单例是 class-实例,它只存在一次。此 class 的行为类似于您的 java 示例。如果您访问 class,所有值在任何地方都是相同的。一般来说,使用 java 风格的 objC 开发并不是一个好的建议。编写好的代码总是有另一种选择。要将数据从 class 传输到另一个,您可以使用 NSNotifications。另一个好处是使用委托(搜索委托和@protocol)。

在您的特殊情况下,以下代码应该有效。我们称之为单例。这是一个 class,只有一个实例并且可以从任何地方访问:

.h 文件:

#import <Foundation/Foundation.h>
@interface MySingletonClass : NSObject

@property(nonatomic, strong) NSMutableArray *list;
+(MySingletonClass*) sharedInstance;
@end

.m 文件:

#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

现在你可以在任何地方导入这个class

#import "MySingletonClass.h"

并像这样使用它:

[[MySingletonClass sharedInstance].list addObject:@"Example object"]; 

如果您习惯了 'java',您可以切换到 swift。语法更类似于 java.

免责声明:您在 main.m 中编写代码的事实表明您已经在 Xcode 中启动了一个控制台应用程序项目,但并未尝试编写 Cocoa 或 Cocoa Touch 应用程序。

如果您尝试为 OS X 或 iOS 编写 Cocoa 或 Cocoa Touch 应用程序,您的 main.m 文件应该类似于这个:

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

而且您应该在修改它之前知道您在做什么(99.999% 的时间,您永远不需要为 Cocoa/Cocoa Touch 应用程序触摸此文件)。

所以重申一下,这个答案假定您正在编写一个简单的控制台应用程序来自学一些 Objective-C 基础知识。

如果这个假设不正确,你的问题应该被关闭,因为你问的不清楚。


回答

Objective-C 是 C 的严格超集。因此,我们可以在 C 中做的任何事情,我们也可以在 Objective-C 中做。这包括在全局范围内声明变量。这与 Java 不同。在 Java 中,一切都是对象——一切都驻留在 class 文件中。这包括 main 方法。示例:

public class MyApplication
{ 
    public static Object[] myArray;
    public static void main(String [] args)
    {
        // execution begins here
    }

}

这与 Objective-C 不同。并非所有内容都必须位于 Objective-C 中的 class 中。如果我们想要 Objective-C 中的全局变量,我们只需在全局范围内声明它即可。示例:

main.m

#import <Foundation/Foundation.h>

NSMutableArray * myArray;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        myArray = [NSMutableArray array];
    }
    return 0;
}

当我们这样声明时,我们可以在 main.m 中的任何地方使用 myArray

不过让我们在这里注意一些事情...如果它不是 class 的成员,则它被称为函数,而不是方法。所以我们也可以在这个全局范围内声明 C 风格的 functions

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

一切正常。如果我们 运行 这个程序,我们将得到以下输出:

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"
)

但是...这不是正确的方法...

我只是向您演示了如何完全按照您的要求进行操作,但我们真的不想以这种方式做事。这是使用全局变量作为一切问题答案的途径。这是使用单例作为解决一切问题的途径。这是编写具有 12 个不同单例 classes 的应用程序的路径(我已经看到了)。这不是通往斯巴达的道路——这是通往疯狂的道路。

相反,我们应该处理对象的实例。

我们的函数(或者当我们升级到 classes 时,我们的方法)应该传递一个参数,我们不应该在全局范围内有一个变量。

所以,让我们摆脱全局变量并修改我们的方法:

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

运行 这个程序给我们相同的输出:

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"
)

也让我们的代码更加灵活。我们的函数 appendHelloWorld 与基于代码的 this 并没有紧密耦合。它可以从这里插入到其他任何地方,并完全按照预期执行。它没有耦合到任何东西。它会将字符串 @"Hello World" 放到它传递的任何可变数组中。虽然 arrayCount() 确实没有必要(仅以它为例),但我们可以对它说同样的话。

我们不想使用全局变量,也不希望函数或方法与全局变量紧密耦合。我们需要熟悉实例化对象实例并将这些对象传递给需要它们的方法或函数的想法。

当多个 classes 访问一些共享内存存储很重要时,应该使用单例 而不是 --NO--这不是单身人士的目的。当您需要确定 class 在应用程序的生命周期中只实例化一个实例时,应使用单例。