【问题标题】:Some questions about Objective-C, Cocoa and Xcode关于 Objective-C、Cocoa 和 Xcode 的一些问题
【发布时间】:2009-07-26 19:46:44
【问题描述】:
  1. 用object初始化一个NSMutableArray数组时,为什么要在最后加上nil?

    例如

    NSMutableArray *fruits = [[NSMutableArray alloc] initWithObjects:apple,
                                                                     watemelon,
                                                                     nil];
    
  2. 在 Xcode 中,当使用自动完成来填写函数变量时。跳转到下一个变量的快捷键是什么?

    例如:

    Fruit *fruit3 = [[Song alloc] initWithName:(NSString *)n
                                   description:(NSString *)d    
    

    我正在输入initWithName 函数,它会自动完成。所以,我直接按tab就可以到n,完成之后怎么去d

  3. 如果我有一个带有问题 1 中的水果对象的水果数组。水果对象是 NSObject 子类。为什么我的 Fruit 类中的名称属性的 @property 声明中有“副本”。

    当我知道默认情况下水果对象将驻留在自己的内存空间中时。因为我正在创建一个水果指针。 Fruit *apple = [[Fruit alloc]....

  4. NSString *a = @"Hello Everyone";

    @"" 有什么作用?

    它会做这样的事情吗:

    [[NSString alloc] initWithValue:"ok"];
    

【问题讨论】:

    标签: objective-c cocoa xcode


    【解决方案1】:
    1. 该方法是使用 C 中的可变参数列表(C 函数声明中的,...)实现的,在这种情况下,参数的数量是未知的。因此,您必须使用 nil (0) 值关闭列表。
    2. 控制-/
    3. 因为 NSArray 是一个指针数组。如果您分配数组而不复制它,则其他对象(引用该数组)可以修改该数组。通过复制,您可以确定数组的元素不会在您的班级之外更改。
    4. @"xyz" 结构将返回一个自动释放的 NSString。所以它将类似于 [[[NSString alloc] initWithUTF8String:"xyz"] autorelease]。

    【讨论】:

    • 请注意,在 Snow Leopard XCode 中,您只需在已完成的参数之间切换即可。
    • 这里的答案 #3 不清楚。您建议copy 通过进行深层复制来防止更改元素,但这根本不是真的,我怀疑这就是您的意思。 copy 的原因是为了防止对数组本身进行更改——阻止在你背后添加/删除对象。即使你的属性是 NSArray*,你也需要这个,因为传入的数组仍然可以是可变数组。
    【解决方案2】:
    1. C 中的变量参数(因此,在 Objective-C 中)是非常原始的。该方法仅获取指向堆栈的指针,并且必须具有一种机制来计算参数的类型和数量。对于 printf 和类似的,它是格式代码;对于 -initWithObjects:,它是一个标记,即表示列表末尾的已知值,在本例中为 nil。
    2. Preferences->Key Bindings->Text Key Bindings->Code Sense Select Next Placeholder.
    3. 考虑以下几点:
    NSMutableString *str = [NSMutableString stringWithString:@"Fred"];
    fruit.name = str; // Legal; NSMutableString is a subclass of NSString
    [str setString:@"Barney"];

    如果fruit.name 未声明为复制属性,则最后一行将更改水果的名称(因为fruit.namestr 指的是同一个对象)。如果它被声明为副本,它们将是单独的对象。 (但是,如果您使用普通的 NSString 对象,则不会进行复制,因为 NSStrings 是不可变的,引用同一个对象没有问题。

    【讨论】:

    • 你对数字 3 的比喻非常清楚,谢谢
    【解决方案3】:

    这是对问题 1 的更完整解释。与 Java、C#、Python、Ruby 等语言相比,C 和 Objective-C 是相当低级的语言。在 C 和 Objective-C 中,允许具有可变数量参数的函数(所谓的 varargsvariadic 函数),但该语言没有提供知道数量的机制实际传入的参数。高级语言通常通过将额外的参数公开为对象列表或数组来提供此功能; C 和 Objective-C 使用一组更有限的宏来一次访问一个额外的参数。

    因此,任何可变参数函数都需要其他方法来确定传递的实际参数数量。 printfscanf 系列函数及其同类使用格式字符串作为固定参数之一,格式字符串中的格式说明符指示传入的额外参数的数量和类型。-initWithObjects: 消息另一方面,Objective-C 类的NSMutableArray 要求额外参数列表以nil 结尾。因此,您不能将nil 作为参数传入(因为它会标记参数列表的结尾),但在这种情况下没关系,因为NSMutableArrays 指定它们不能包含nil 对象。

    在 x86 架构上,可变参数函数是使用 C 调用约定 (cdecl) 实现的,即参数从右到左压入堆栈。调用者负责在调用后清理堆栈。因此,对printf() 的调用可能会被编译成如下内容:

    // ignore the fact that these are invalid calls to printf
    printf("format string 1", a1, a2);
    printf("format string 2", b1, b2, b3);
    
    // The above might get compiled into this:
    push a2
    push a2
    push <address of "format string 1">
    call printf
    add $12. %esp    // restore stack pointer to offset the pushes
    push b3
    push b2
    push b1
    push <address of "format string 2">
    call printf
    add $16, %esp   // restore stack pointer
    

    参数被从右到左而不是从左到右推送的原因是堆栈增长向下 -- x86 push 指令将堆栈指针 esp 递减4字节。因此,当被调用函数检查堆栈时,它将以正确的顺序查看参数。在第一次调用上面的printf 之后,堆栈看起来像这样:

    %esp+0x00:返回地址给调用函数 %esp+0x04: "格式字符串1">的地址 %esp+0x08: a1 %esp+0x0c: a2 %esp+0x10 及以上:来自调用函数的局部变量,堆栈的其余部分

    当被调用函数检查它的参数时,它只是遍历堆栈,但它不知道在哪里停止——调用约定没有指定有多少个参数。该功能只需要通过其他方式知道。大多数函数都有固定数量的参数,这对它们来说很容易,但可变参数函数需要其他方法来确定参数的确切数量。

    如果函数认为传递的参数数量与实际传递的参数数量不匹配(例如,将错误的格式字符串传递给printf),则会导致错误。最可能的情况是该函数将继续向下读取堆栈,并且您将在某处获得垃圾值。更糟糕的是,您的程序可能会崩溃。更糟糕的是,您的程序可能看起来运行正常,但您可能无意中打开了一个安全漏洞。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-15
      • 2012-09-12
      • 1970-01-01
      • 2010-10-04
      • 2011-01-05
      • 2010-12-06
      相关资源
      最近更新 更多