Objective-C 结构体数组

5
我希望有一个类似以下的结构体:
struct foo {
NSString Title;
NSInteger numberOfBooks;
vector of NSStrings Books;
};

foo bar[50];

首先,我想知道如何创建一个字符串数组(每一行包含一本书的书名)。

然后我想知道我编写的代码是否正确。

最后,我能够通过bar[n].Title访问结构体中的元素吗?


我已经遵循了您的评论,并且这是它的样子:

@interface Foo : NSObject {
NSString *name;
NSArray *books;
}

@end

A->name = @"Clancy, Thomas";
A->books = [[NSArray alloc] initWithObjects:@"Book 1"
            @"Book2",nil];
self.authors = [[NSArray alloc] initWithObjects:A, nil];

现在它告诉我实例变量'name'是受保护的。我尝试在定义Foo时编写public:,但它不接受这个。(是的,我刚开始学习Obj-C)。

2
为什么不能在NSArray中使用自定义对象? - user529758
你能举个例子吗?我来自C++,通常这就是我完成类似任务的方式。 - Lord Zsolt
@userXXX 在 @interface Foo: NSObject 中声明 titlenumberOfBooks(这个属性有些冗余,为什么需要它呢?)和 books 属性。然后创建这些对象的实例并将它们存储在 NSArrayNSMutableArray 中。 - user529758
如果你正在使用ARC,那么在结构体和联合体中,如果没有显式的__unsafe_unretained生命周期限定符,则ObjC成员是被禁止的。 - CodaFi
好的,我已经尝试了你说的,这是结果: - Lord Zsolt
正如@adrusi的回答所示,我们使用“.”而不是“->”来访问对象的属性。此外,我们更喜欢以小写字母开头命名变量。因此,例如,a.name = @"Clancy, Thomas"; - rob mayoff
3个回答

9
很不幸,启用ARC的Objective-C不允许在C结构体内部存储对象。而且,Objc只允许在NSArrays中(相当于向量)存储对象。
解决方案的第一步是创建一个类,而不是一个结构体:
// Foo.h
#import <Foundation/Foundation.h>

@interface Foo : NSObject
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSArray *books;
// no need to keep the number of items in an NSArray as a separate variable,
// NSArray does that for you.

- (id)initWithTitle:(NSString *)title books:(NSArray *)books;

@end

// Foo.m
#import "Foo.h"

@implementation Foo
@synthesize title;
@synthesize books;

- (id)initWithTitle:(NSString *)title books:(NSArray *)books
{
  if (self = [super init]) {
    self.title = title;
    self.books = books;
  }
  return self;
}

@end

请注意,所有的对象类型都是指针。这是因为 objc 中的所有对象都是堆分配的,所以你不能使用不是指针的对象类型。此外,请注意类型 NSArray 没有参数化。这是因为 objc 没有泛型或模板的概念,所以数组中的内容可以是任何对象。通常这不是问题,但偶尔会出现类型错误无法在编译时被捕获的情况。
要保留这些 Foo 对象的数组很简单。你可以像这样创建一个 C 数组:Foo *bar[50]; 但我认为它不能与 ARC 一起使用,而且在 Objective-C 中这绝对不是正确的方法。正确的方法是使用另一个 NSArray:NSArray *bar = [[NSArray alloc] init]; 或者用 objc2.0 语法:NSArray *bar = @[];
下面是一些可能对你有帮助的注释:
@property 和 @synthesize 是制作实例变量和访问器方法的快捷方式。如果你想从类外部(或者说实现文件)访问实例变量,则需要访问器方法。strong 标志告诉引用计数器这是一个 strong 引用,就像 c++ boost 库中的 shared_ptr。我不知道 nonatomic 做什么,但据说你需要它。
方法 initWithTitle:books: 类似于构造函数,但 objc 没有构造函数的概念。实现会用 [super init] 调用超类的构造函数(假设 self 是对象的 c++ 引用)。构造函数可以返回 nil,所以你需要 if 块。
代码行 self.title = title; 技术上是 [self setTitle:title]; 的缩写,它使用由 @property 和 @synthesize 自动生成的方法。

1
它们在结构体中并不被禁止,只是受到限制。 - CodaFi
"Objc并不仅仅允许在NSArrays中使用对象" - 这是不正确的。它们也可以在C数组中使用。甚至苹果自己的ARC指南也解释了如何在动态C数组中正确使用它们。 - Mecki

4
可以将C结构体封装到通用的NSValue容器中。您可以使用[NSValue value:withObjCType:]编码值,使用[NSValue getValue:]获取该值。然后,您可以将新的NSValue *对象添加到任何Objective-C容器中。
请参见此示例,并阅读此文了解有关类型编码的更多信息。
请注意,封装/取消封装不是免费的,因此如果您正在编写性能关键的应用程序,则建议您使用std::vector或其他C ++容器来避免每次存储/获取项目时执行此过程。

0
在Objective-C中,与其使用结构体,不如使用NSDictionaries或自定义对象更好。扩展H2CO3的评论 - 你可以声明一个BookCollection类。

BookCollection.h

#import <Foundation/Foundation.h>


@interface BookCollection: NSObject

@property (nonatomic, strong) NSString* title
@property (nonatomic, strong) NSArray* books
   //use NSArray* for a static array or NSMutableArray* for a dynamic array

@end

BookCollection.m

#import "BookCollection/BookCollection.h"

@implementation BookCollection

@end

你的实现是空的,因为你的对象只有公共属性而没有自定义方法。我们使用@property语法来声明实例变量,因为这带来了许多优点,例如内置的getter和setter。(有关详细信息,请参见我的答案此处

与C++不同,你可以在集合类中混合对象类型,因此如果你添加标题、书籍和其他书籍集合,你的books数组不会报错:你的代码应该确保一致性。我们不声明数组大小。对于静态NSArray,大小在创建时被隐含地确定,对于动态NSMutableArray,没有固定的限制。对于包含五十个书籍集合的集合,请创建一个包含五十个预先存在的集合的NSArray。或者创建一个NSMutableArray,你可以逐步添加。

BookCollection* joyce = [[BookCollection alloc] init];
joyce.title = @"Books by James Joyce";
joyce.books = @["Ulysses",@"Finnegan's Wake"]; //using static NSArray


BookCollection* tolkien = [[BookCollection alloc] init];
tolkien.title = @"Books by J.R.R. Tolkien";
[tolkien.books addObject:@"The Hobbit"];  //using dynamic NSMutableArray
[tolkien.books addObject:@"Lord of the Rings"];

对于一个集合的动态数组:

NSMutableArray* collections = [[NSMutableArray alloc] init];
[collections addObject:joyce];
[collections addObject:tolkien];

对于固定数组,您可以使用文字语法:

NSArray* collections = @[joyce, tolkien];

对于numberOfBooks,只需使用数组的count属性即可。

int numberOfBooks = joyce.books.count;

如果你想确保集合是带有标题和书籍的,请使用自定义初始化器,就像adrusi的答案所示。这不是使对象工作所必需的,但如果你想要保证你的对象具有内容,这是很有用的。

要访问实例变量,请使用[message syntax](原始的obj-C方式)或dot.syntax(适用于@properties)

[tolkien books]
joyce.books

不要使用->箭头语法。请参见此处以获取详细说明。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接