gpt4 book ai didi

iphone - 与 Objective-C 中的头文件和实现文件混淆

转载 作者:行者123 更新时间:2023-12-03 18:17:05 25 4
gpt4 key购买 nike

首先,请原谅这个问题的愚蠢,但我不是来自 C/C++ 背景。我有点不清楚 .h 和 .m 文件在属性方面的角色差异。

我理解接口(interface)的概念,并且我看到部分 .h 文件是实现的接口(interface),但我不清楚的是:

  • 为什么在 {} 大括号之外定义属性/方法?
  • 当我写这样的东西时,我在大括号中定义了什么:

    IBOutlet UITextField *numberField;

    这是接口(interface)中的字段定义吗?
  • 当我将 @Property 行添加到 .h 文件时,这些是 n auto 属性的实际实现还是只是一个接口(interface)蓝图?如果是这样,@syntesis 是实际实现吗?

  • 我想我最大的困惑似乎是,如果我想要一个属性,我将在接口(interface)大括号中的三个不同位置(1)定义我需要的内容,(2)大括号外的@property 和(3)@synthesis 中的.m 文件。这似乎冗长,但如果我能弄清楚这三个部分的作用就好了。

    干杯,克里斯。

    最佳答案

    我将在下面回答您的问题,但也许学习这些东西的最好方法是阅读一些针对该语言新手的用户友好注释,例如 the Learn Objective-C tutorialcocoadevcentral .

    一个例子

    我想用一个例子来帮助回答你的问题(我喜欢通过例子学习)。假设您是一名教师,正在编写一个程序,该程序会询问学生一个特定的是/否问题,并跟踪有多少人答对了,以及有多少学生问了这个问题。

    这是这个类的一个可能的接口(interface):

    @interface Question : NSObject {
    NSString* questionStr;
    int numTimesAsked;
    int numCorrectAnswers;
    }

    @property (nonatomic, retain) NSString* questionStr;
    @property (nonatomic, readonly) int numTimesAsked;
    @property (nonatomic) int numCorrectAnswers;
    @property (nonatomic) int numWrongAnswers;

    - addAnswerWithTruthValue: (BOOL) isCorrect;
    @end

    大括号内的三个变量是实例变量,类的每个实例对于这些变量都有自己的值。大括号之外但之前的所有内容 @end是方法的声明(包括 @property 声明)。

    (旁注:对于许多对象,拥有 retain 属性很有用,因为您想避免复制对象的开销,并确保在使用它时不会释放它。 retain 是合法的一个 NSString 就像在这个例子中一样,但是 it is often considered good practice to use copy instead of retain 因为一个 NSString* 实际上可能指向一个 NSMutableString 对象,当你的代码希望它保持不变时,它可能会在以后改变。)

    什么 @property

    当您声明 @property 时,你在做两件事:
  • 在类的接口(interface)中声明 setter 和 getter 方法,以及
  • 指示 setter 和 getter 的行为方式。

  • 对于第一个,知道这一行就足够了:
    @property (nonatomic, retain) NSString* questionStr;

    基本上与此相同:
    - (NSString*) questionStr;                           // getter
    - (void) setQuestionStr: (NSString) newQuestionStr; // setter

    在标题中。您实际上是在声明这两种方法;您可以直接调用它们,也可以使用点符号作为快捷方式为您调用它们。

    “基本相同”中的“基本”部分是 nonatomic等关键字给出的额外信息。和 retain .
    nonatomic关键字表示它们不一定是线程安全的。常见的 retain关键字表示对象保留设置的任何值,并在释放之前的值时释放它们。

    例如:
    // The correct answer to both questions is objectively YES.
    Question* myQuestion = [[Question alloc] init];
    NSString* question1 = [[NSString alloc] initWithString:@"Is pizza tasty?"];
    // question1 has retain count of 1, from the call to alloc
    myQuestion.questionStr = question1;
    // question1 now has a retain count of 2
    NSString* question2 = [[NSString alloc] initWithString:@"Free iPhone?"];
    myQuestion.questionStr = question2;
    // question1 has a retain count of 1, and question2 has retain count of 2

    如果 @property questionStr的声明已 assign相反,则所有 myQuestion.questionStr =语句根本不会对保留计数进行任何更改。

    您可以 read a little more about properties here .

    什么 IBOutletIBAction

    这些基本上是无操作词,它们只是告诉 Interface Builder 要注意头文件的哪些部分。 IBOutlet当编译器查看它时,字面上变成了一个空字符串,并且 IBAction成为 void返回值。不过,我们确实需要它们与 Interface Builder 一起工作,所以它们很重要——只是对编译器不重要。

    关于 C 结构和箭头与点符号的快速说明

    顺便说一下,Objective-C 对象的数据部分与 C 结构非常相似。如果你有一个指向 C 结构体的指针,你可以使用箭头符号 ->引用结构的特定部分,如下所示:
    struct MyStructType {
    int i;
    BOOL b;
    };
    struct MyStructType* myStruct;
    myStruct->i = 3;
    myStruct->b = TRUE; // or YES in Objective-C.

    相同的语法在 Objective-C 中的工作方式相同:
    Question* question = [[Question alloc] init];
    question->questionStr = @"Is this a long answer?"; // YES

    但是当您这样做时,与点表示法不同,在幕后不会发生任何方法调用。使用点符号,您调用的是 setter(如果之后没有 =,则调用 getter),并且这两行是相同的:
    question.questionStr = @"Chocolate?";
    [question setQuestionStr:@"Chocolate?"];

    避免使用箭头表示法而使用点表示法通常是一个好主意,因为点表示法允许您强制执行有效状态——例如,始终保留您的类所具有的指针。您甚至可以通过将您的实例变量声明为 @private 来禁止其他人使用箭头符号。 ;如果你声明 @property,他们仍然可以使用 getter 和 setter 来访问它。为了它。

    @synthesize 做什么

    现在,当你开始实际实现你的类时, @synthesize说诸如“确保为该属性实现了 getter 和 setter。”它没有说“为我实现这两个”,因为编译器足够礼貌,会先检查您自己的实现,然后只填写您遗漏的部分。您不必使用 @synthesize根本没有,即使你使用 @property出 wazoo - 如果你喜欢那种事情,你总是可以为你的 setter 和 getter 提供你的实现。

    您可能在 Question 中注意到了上面的接口(interface)有一个不是实例变量的属性( numWrongAnswers ),这很好,因为您只是在声明方法。在此处的示例代码中,您可以看到这实际上是如何工作的:
    @implementation Question

    @synthesize questionStr, numTimesAsked, numCorrectAnswers;

    - (void) setNumCorrectAnswers: (int) newCorrectAnswers {
    // We assume the # increases, and represents new answers.
    int numNew = newCorrectAnswers - numCorrectAnswers;
    numTimesAsked += numNew;
    numCorrectAnswers = newCorrectAnswers;
    }

    - (int) numWrongAnswers {
    return numTimesAsked - numCorrectAnswers;
    }

    - (void) setNumWrongAnswers: (int) newWrongAnswers {
    int numNew = newWrongAnswers - self.numWrongAnswers;
    numTimesAsked += numNew;
    }

    - (void) addAnswerWithTruthValue: (BOOL) isCorrect {
    if (isCorrect) {
    self.numCorrectAnswers++;
    } else {
    self.numWrongAnswers++;
    }
    }

    @end

    这里发生的一件事是我们伪造了一个名为 numWrongAnswers 的实例变量。 ,如果我们将它存储在类中,这将是冗余信息。既然我们知道 numWrongAnswers + numCorrectAnswers = numTimesAsked在任何时候,我们只需要存储这三个数据点中的任意两个,并且我们总是可以通过使用我们知道的两个值来考虑另一个。这里的重点是要理解 @property声明实际上只是声明一个 setter 和 getter 方法,它们通常对应于一个实际的实例变量——但并非总是如此。 @synthesize关键字默认对应一个实际的实例变量,所以编译器很容易为你填写实现。

    分开的理由 .h.m文件

    顺便说一句,在一个文件( .h 头文件)中声明方法并在另一个文件( .m 或方法文件)中定义它们的实现的重点是帮助解耦代码。例如,如果您只更新一个 .m文件中,你不必重新编译另一个 .m文件,因为它们的目标代码将保持不变——这可以节省时间。另一个优点是您可以使用仅包含头文件和预编译目标代码的库,甚至可以使用需要头文件的动态库,以便编译器知道存在哪些方法,但这些方法甚至没有链接到与您的可执行文件。当您第一次开始编码时,这些优势很难被理解,但只是实现的逻辑分解和封装在短时间内变得有用。

    我希望这有帮助!

    关于iphone - 与 Objective-C 中的头文件和实现文件混淆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1449698/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com