学习Objective-C:新手指南 – wiki基地


学习 Objective-C:一本面向绝对新手、内容翔实的入门指南

引言:为何以及如何踏上 Objective-C 的学习之旅?

在移动应用和桌面软件开发的广阔世界中,Objective-C 曾长期占据着举足轻重的地位,尤其是在苹果公司的生态系统(iOS 和 macOS)中。尽管近年来 Swift 语言逐渐成为苹果平台的首选开发语言,但 Objective-C 仍然拥有庞大的现有代码库,许多核心框架和底层技术依然基于 Objective-C 构建。因此,学习 Objective-C 不仅能帮助你维护和理解现有项目,更能加深你对苹果开发体系底层原理的理解,这对于成为一名资深的苹果平台开发者来说,是一笔宝贵的财富。

本指南旨在为那些对编程知之甚少或刚刚入门的读者提供一个全面、详细的 Objective-C 学习路径。我们将从零开始,逐步探索 Objective-C 的基础概念、语法特性、核心框架以及开发工具的使用。准备好你的求知欲,让我们一起踏上这段充满挑战与乐趣的学习之旅吧!

第一章:认识 Objective-C——历史、特性与地位

在深入学习语法之前,了解 Objective-C 的背景是很有必要的。这有助于你理解它独特的设计哲学。

1.1 Objective-C 的起源与历史

Objective-C 诞生于 20 世纪 80 年代,由 Brad Cox 和 Tom Love 在 Stepstone 公司开发。它是在 C 语言的基础上,引入了 Smalltalk 风格的面向对象特性。Smalltalk 是一种纯粹的面向对象语言,以其动态性、消息传递机制和高度灵活性著称。Objective-C 巧妙地将 C 语言的效率和过程式编程能力与 Smalltalk 的面向对象特性结合起来。

Objective-C 最重要的发展阶段与其在 NeXT Computer 公司的应用紧密相关。史蒂夫·乔布斯离开苹果后创建了 NeXT,Objective-C 成为了 NeXTSTEP 操作系统的主要开发语言。NeXTSTEP 系统及其开发框架(后来的 Cocoa)为现代 macOS 和 iOS 打下了坚实的基础。当苹果公司在 1996 年收购 NeXT 后,Objective-C 也随之回到了苹果,并成为 macOS 和 iOS 应用开发的主流语言,直至 Swift 出现。

1.2 Objective-C 的核心特性

  • C 语言的超集: Objective-C 完全兼容 C 语言的语法和特性。你可以在 Objective-C 代码中直接编写 C 代码。这意味着 Objective-C 拥有 C 语言的底层操作能力和效率。
  • Smalltalk 风格的面向对象: 这是 Objective-C 最独特的方面。它引入了类 (Class)、对象 (Object)、消息传递 (Messaging) 等概念,但其消息传递机制与 C++ 或 Java 的方法调用有所不同,更具动态性。
  • 动态性 (Runtime): Objective-C 是一门非常动态的语言。许多在编译时确定的事情,在 Objective-C 中可以延迟到运行时。这使得 Objective-C 具有很强的灵活性和可扩展性(例如,方法交换 Method Swizzling)。
  • 丰富的框架: 苹果为 Objective-C 提供了强大的框架支持,如 Foundation (处理基本数据类型、集合、文件系统等)、UIKit (iOS 用户界面)、AppKit (macOS 用户界面) 等。这些框架极大地简化了开发过程。

1.3 Objective-C 与 Swift:现在的地位

Swift 是苹果在 2014 年推出的新一代编程语言,旨在取代 Objective-C 成为苹果平台的主要开发语言。Swift 吸收了 Objective-C 的优点,同时改进了语法,提高了安全性,并提供了更好的性能。苹果官方也大力推广 Swift。

那么,Objective-C 现在还值得学吗?答案是肯定的,原因如下:

  • 海量现有代码: 许多公司和项目仍然有大量的 Objective-C 代码需要维护、更新或扩展。
  • 框架基础: 苹果的许多核心框架最初是用 Objective-C 编写的,即使在 Swift 中调用这些框架,理解其 Objective-C 原理(如内存管理、消息传递模式)也会非常有帮助。
  • 学习垫脚石: Objective-C 的许多面向对象概念和苹果的框架设计思想与 Swift 是相通的。先学习 Objective-C 可以为你后续学习 Swift 打下坚实的基础。
  • 深入底层: 对于希望深入理解苹果运行时、进行底层开发或逆向工程的开发者来说,掌握 Objective-C 是必不可少的。

第二章:准备开发环境——安装 Xcode

进行 Objective-C 开发,你需要在 macOS 系统上安装苹果官方的集成开发环境 (IDE)——Xcode。Xcode 包含了编译器、调试器、模拟器以及各种开发工具,是苹果平台开发者的必备利器。

2.1 macOS 系统要求

Xcode 只能运行在 macOS 系统上。不同版本的 Xcode 对 macOS 系统版本有最低要求。通常,你需要一台配置较新的 Mac 电脑来运行最新版本的 Xcode。

2.2 安装 Xcode

安装 Xcode 非常简单:

  1. 打开 Mac App Store: 在你的 Mac 上找到并打开 App Store 应用。
  2. 搜索 Xcode: 在搜索框中输入 “Xcode”。
  3. 下载并安装: 找到 Xcode 应用,点击 “获取”,然后点击 “安装”。Xcode 的体积较大,下载和安装可能需要一些时间,取决于你的网络速度。
  4. 启动 Xcode: 安装完成后,在 “应用程序” 文件夹中找到 Xcode 图标并双击启动。首次启动时,Xcode 会进行一些初始化配置。

2.3 Xcode 初览

启动 Xcode 后,你会看到一个欢迎窗口。你可以选择:

  • Create a new Xcode project: 创建一个新的项目。
  • Clone an existing project: 从版本控制系统(如 Git)克隆一个现有项目。
  • Open a project or file: 打开一个已有的项目或文件。

对于初学者,我们可以先创建一个简单的项目来熟悉环境。选择 “Create a new Xcode project”。

2.4 创建一个简单的命令行项目

为了专注于 Objective-C 语言本身,我们先创建一个不涉及图形界面的命令行工具:

  1. 在项目模板选择界面,选择 macOS -> Command Line Tool。点击 Next。
  2. Product Name: 输入项目名称,例如 “ObjectiveCPrimer”。
  3. Organization Identifier: 输入一个反向域名格式的组织标识符,例如 “com.yourcompany” (用你自己的公司或名字替换 yourcompany)。这将构成你的 Bundle Identifier。
  4. Language: 确保选择了 “Objective-C”。
  5. Use Core Data: 不勾选。
  6. Include Unit Tests: 不勾选。
  7. Include UI Tests: 不勾选。
  8. 点击 Next。
  9. 选择一个文件夹来保存你的项目,点击 Create。

Xcode 会为你创建一个基本的项目结构,并在编辑器中打开 main.m 文件。这个文件就是你编写 Objective-C 代码的起点。

第三章:Objective-C 的 C 语言基础

正如前文所述,Objective-C 是 C 语言的超集。因此,理解一些基本的 C 语言概念对于学习 Objective-C 至关重要。如果你已经熟悉 C 语言,可以快速浏览本章。

3.1 基本数据类型

C 语言提供了一些基本的数据类型来存储不同类型的数据:

  • int: 整数,如 -1, 0, 100。
  • float: 单精度浮点数,如 3.14f。
  • double: 双精度浮点数,精度更高,如 3.14159。
  • char: 字符,如 ‘A’, ‘b’。
  • BOOL: Objective-C 特有的布尔类型,用于表示真 (YES) 或假 (NO)。在底层,YES 通常是 1,NO 通常是 0。
  • void: 表示没有类型,常用于函数参数或返回值表示“无”。

3.2 变量

变量是用来存储数据的容器。在使用变量之前,需要先声明它,即指定变量的类型和名称。

objectivec
int age = 30; // 声明一个整型变量 age 并初始化为 30
float weight = 65.5f; // 声明一个单精度浮点型变量 weight
char initial = 'J'; // 声明一个字符型变量 initial
BOOL isStudent = YES; // 声明一个布尔型变量 isStudent

变量名需要遵循一定的规则:只能包含字母、数字和下划线,且不能以数字开头。

3.3 运算符

C 语言提供了各种运算符:

  • 算术运算符: + (加), - (减), * (乘), / (除), % (取模)
  • 关系运算符: == (等于), != (不等于), > (大于), < (小于), >= (大于等于), <= (小于等于)。这些运算符的结果是布尔值 (YESNO)。
  • 逻辑运算符: && (逻辑与), || (逻辑或), ! (逻辑非)。
  • 赋值运算符: = (赋值), +=, -=, *= 等复合赋值运算符。
  • 自增/自减运算符: ++ (自增 1), -- (自减 1)。

objectivec
int a = 10, b = 5;
int sum = a + b; // sum 为 15
BOOL isGreater = (a > b); // isGreater 为 YES
BOOL logicalAnd = (isGreater && isStudent); // 假设 isStudent 为 YES, logicalAnd 为 YES
a++; // a 变为 11

3.4 控制流程

控制流程语句用于控制程序的执行顺序。

  • 条件语句: if, else if, else

    “`objectivec
    if (age >= 18) {
    NSLog(@”成年人”); // NSLog 是 Objective-C 中用于打印输出的函数
    } else {
    NSLog(@”未成年人”);
    }

    if (score >= 90) {
    NSLog(@”优秀”);
    } else if (score >= 60) {
    NSLog(@”及格”);
    } else {
    NSLog(@”不及格”);
    }
    ``
    * **循环语句:**
    for,while,do-while`

    “`objectivec
    // for 循环
    for (int i = 0; i < 5; i++) {
    NSLog(@”循环次数: %d”, i); // %d 是格式化字符串,用于输出整数
    }

    // while 循环
    int count = 0;
    while (count < 3) {
    NSLog(@”while 循环次数: %d”, count);
    count++;
    }

    // do-while 循环 (至少执行一次循环体)
    int j = 0;
    do {
    NSLog(@”do-while 循环次数: %d”, j);
    j++;
    } while (j < 0); // 条件为假,但循环体仍执行一次
    ``
    * **跳转语句:**
    break(跳出循环),continue(跳过当前循环的剩余部分,进入下一次循环),return` (从函数返回)

3.5 函数

函数是一段执行特定任务的代码块。C 语言中的函数定义包括返回值类型、函数名、参数列表和函数体。

“`objectivec
// 函数声明 (可选,但推荐在使用前声明)
int add(int a, int b);

// 函数定义
int add(int a, int b) {
return a + b;
}

// 在 main 函数中调用
int main(int argc, const char * argv[]) {
@autoreleasepool {
int result = add(5, 3);
NSLog(@”5 + 3 = %d”, result);
}
return 0;
}
“`

3.6 指针 (初步了解)

指针是一个变量,它存储了另一个变量的内存地址。虽然现代 Objective-C 应用开发中直接操作指针的场景相对较少(尤其是自动内存管理 ARC 启用后),但理解指针的概念对于理解某些底层机制(如字符串、数组的传递)是必要的。

“`objectivec
int number = 10;
int *pointerToNumber = &number; // & 操作符用于获取变量的地址

NSLog(@”number 的值: %d”, number); // 输出 10
NSLog(@”number 的地址: %p”, &number); // %p 用于输出地址
NSLog(@”pointerToNumber 存储的地址: %p”, pointerToNumber); // 输出 number 的地址
NSLog(@”通过 pointerToNumber 获取 number 的值: %d”, *pointerToNumber); // * 操作符用于获取指针指向地址的值
“`

第四章:Objective-C 的面向对象核心

现在,我们进入 Objective-C 的核心部分:面向对象编程。这是 Objective-C 在 C 语言基础上添加的最重要的特性。

4.1 类和对象

  • 类 (Class): 类是对象的蓝图或模板,它定义了对象的属性(数据)和行为(方法)。例如,”汽车” 是一个类。
  • 对象 (Object): 对象是类的一个实例。它是根据类的定义创建的具体实体。例如,”我的红色福特汽车” 是一个对象。

在 Objective-C 中,类的定义通常分为两个部分:接口 (.h 文件) 和实现 (.m 文件)。

4.2 类接口 (.h 文件)

类接口声明了类的名称、继承关系、实例变量和方法。

“`objectivec
// Person.h 文件

import // 引入 Foundation 框架头文件

// @interface 是定义类接口的关键字
// Person 是类名
// : NSObject 表示 Person 类继承自 NSObject 类。NSObject 是 Foundation 框架中所有 Objective-C 类的基类。
@interface Person : NSObject

// 实例变量 (Instance Variables / ivars)
// 存储对象的状态。通常在类接口中使用 {} 声明,或者更常见的是使用 @property 声明属性。
// {
// NSString *name; // 人的名字
// int age; // 人的年龄
// }

// 属性 (Properties)
// @property 是一种更现代、更方便的方式来声明实例变量,并自动生成存取方法 (getter 和 setter)。
// nonatomic 表示非原子性,线程不安全但性能更高 (在单线程或明确控制线程的场景常用)。
// strong 表示强引用,对象会被持有,直到不再有强引用指向它 (ARC)。
// copy 表示复制对象,通常用于 NSString 等可变/不可变字符串,防止原对象被修改影响副本。
// assign 用于基本数据类型 (int, float, BOOL 等) 或 C-style 结构体。
// weak 表示弱引用,不持有对象,当对象不再被强引用时,会自动设置为 nil (ARC)。
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) int age;

// 方法声明 (Method Declarations)
// ‘-‘ 开头表示实例方法 (Instance Method),需要通过类的对象来调用。
// ‘+’ 开头表示类方法 (Class Method),可以通过类名直接调用。

// 实例方法:打招呼
// -(返回值类型) 方法名:(参数类型)参数名;
-(void)sayHello;

// 实例方法:带参数的打招呼
// -(void)greet:(NSString *)greeting; // 参数 greeting 是一个 NSString 对象

// 实例方法:多个参数
// -(void)walk:(int)steps speed:(float)speed; // 方法名和参数名交错
-(void)performAction:(NSString *)action withValue:(int)value;

// 类方法:创建一个默认 Person 对象
// +(返回值类型) 方法名;
+(instancetype)defaultPerson; // instancetype 表示返回当前类的实例

@end // @end 结束类接口定义
“`

4.3 类实现 (.m 文件)

类实现包含了类的方法的具体代码。

“`objectivec
// Person.m 文件

import “Person.h” // 引入对应的头文件

// @implementation 是定义类实现的关键字
// Person 是类名
@implementation Person

// @synthesize name = _name; // 在早期 Objective-C 中需要手动合成 ivar 和 property
// @synthesize age = _age; // 在 ARC 和现代编译器中,@property 会自动合成对应的 ivar (_name, _age)

// 方法实现 (Method Implementations)

// 实例方法 sayHello 的实现
-(void)sayHello {
// 在方法实现中,可以通过 self 关键字引用当前对象
// 可以直接通过属性名访问对应的 ivar
// 或者使用点语法访问属性 (实际上是调用 getter/setter 方法)
// NSLog 是 Objective-C 中用于打印输出的函数
// %@ 是用于输出对象的格式化符
NSLog(@”Hello, my name is %@ and I am %d years old.”, self.name, self.age);
}

// 实例方法 performAction:withValue: 的实现
-(void)performAction:(NSString *)action withValue:(int)value {
NSLog(@”%@ is performing action: %@ with value %d.”, self.name, action, value);
}

// 类方法 defaultPerson 的实现
+(instancetype)defaultPerson {
// alloc 是 NSObject 类的方法,用于分配对象的内存空间
// init 是 NSObject 类的方法,用于初始化对象
// alloc 和 init 通常连用,创建一个新的对象实例
Person *person = [[Person alloc] init];
// 设置对象的属性
person.name = @”Default User”;
person.age = 0;
return person;
}

@end // @end 结束类实现定义
“`

4.4 创建对象和消息传递

在 Objective-C 中,通过向类发送消息来创建对象,然后通过向对象发送消息来调用其方法。这种“消息传递”机制是 Objective-C 与 C++、Java 等语言中“方法调用”的主要区别之一。消息传递更具动态性,即使接收者 (receiver) 在运行时未知,也可以发送消息。

“`objectivec
// 在 main.m 中使用 Person 类

import

import “Person.h” // 引入 Person 类头文件

int main(int argc, const char * argv[]) {
@autoreleasepool { // 自动释放池,用于管理对象的生命周期 (在 ARC 下仍有作用,主要与 autoreleasepool 机制相关)

    // 创建 Person 类的实例 (对象)
    // 语法:[类名 消息] 或 [对象 消息]
    // [[Person alloc] init] 是向 Person 类发送 alloc 消息,再向返回的内存地址发送 init 消息
    Person *person1 = [[Person alloc] init];

    // 设置对象的属性 (通过点语法,实际上是调用 setter 方法 person1.name = @"Alice"; 等价于 [person1 setName:@"Alice"];)
    person1.name = @"Alice";
    person1.age = 25;

    // 向对象发送消息 (调用方法)
    // 语法:[对象 方法名] 或 [对象 方法名:参数1 参数名2:参数2 ...]
    [person1 sayHello]; // 输出 "Hello, my name is Alice and I am 25 years old."

    [person1 performAction:@"jumping" withValue:10]; // 输出 "Alice is performing action: jumping with value 10."


    // 使用类方法创建对象
    Person *defaultPerson = [Person defaultPerson];
    [defaultPerson sayHello]; // 输出 "Hello, my name is Default User and I am 0 years old."


    // nil 对象的消息传递
    // 向 nil 对象发送消息是安全的,不会崩溃。
    // 如果方法有返回值,对象类型返回 nil,基本数据类型返回 0 或等效值。
    Person *nilPerson = nil;
    [nilPerson sayHello]; // 不会崩溃,什么也不做
    int nilAge = [nilPerson age]; // nilAge 为 0
    NSString *nilName = [nilPerson name]; // nilName 为 nil (NULL)

}
return 0;

}
“`

4.5 继承

Objective-C 支持单继承,一个类只能直接继承自一个父类。子类可以继承父类的实例变量和方法,并可以添加新的实例变量和方法,或者重写父类的方法。

“`objectivec
// Student.h 文件

import “Person.h” // Student 继承自 Person,所以需要引入 Person 的头文件

@interface Student : Person // Student 继承自 Person

@property (nonatomic, strong) NSString studentId;
@property (nonatomic, strong) NSString
major;

// 重写父类的方法
-(void)sayHello;

// 新增方法
-(void)study;

@end
“`

“`objectivec
// Student.m 文件

import “Student.h”

@implementation Student

// 重写父类方法
-(void)sayHello {
// 可以通过 super 关键字调用父类的方法
[super sayHello]; // 先调用父类的 sayHello 方法
NSLog(@”I am a student with ID %@, majoring in %@.”, self.studentId, self.major); // 然后执行子类自己的逻辑
}

// 新增方法
-(void)study {
NSLog(@”%@ is studying hard for %@.”, self.name, self.major);
}

@end
“`

使用 Student 类:

“`objectivec
// 在 main.m 中

import

import “Student.h”

int main(int argc, const char * argv[]) {
@autoreleasepool {
Student *student1 = [[Student alloc] init];
student1.name = @”Bob”;
student1.age = 20;
student1.studentId = @”S12345″;
student1.major = @”Computer Science”;

    [student1 sayHello]; // 调用重写后的 sayHello 方法
    [student1 study];    // 调用新增的 study 方法
}
return 0;

}
“`

第五章:内存管理

内存管理是 Objective-C 开发中的一个重要话题。开发者需要负责分配对象所需的内存,并在不再需要时释放这些内存,以避免内存泄漏(Memory Leak,不再使用的内存没有释放)和野指针错误(Dangling Pointer,试图访问已释放的内存)。

Objective-C 的内存管理经历了几个阶段:

  1. 手动引用计数 (Manual Retain-Release / MRR): 需要开发者手动调用 retain (增加引用计数)、release (减少引用计数)、autorelease (放入自动释放池)。这是 Objective-C 早期的主要内存管理方式。
  2. 垃圾回收 (Garbage Collection): 在 macOS 上短暂出现,但在 iOS 上从未支持,并且已经被废弃。
  3. 自动引用计数 (Automatic Reference Counting / ARC): 在 iOS 5 和 macOS 10.7 引入,是目前主流的内存管理方式。编译器在编译时自动插入 retainreleaseautorelease 代码。开发者无需手动管理引用计数,但仍需理解其原理。

5.1 自动引用计数 (ARC)

在 ARC 下,编译器会跟踪对象的引用计数。当一个对象被一个“强引用”指针指向时,其引用计数加一;当一个强引用指针不再指向该对象时,其引用计数减一。当对象的引用计数变为零时,系统会自动销毁该对象并回收内存。

开发者需要理解和正确使用以下关键字:

  • strong (默认): 强引用。持有对象,使其引用计数加一。当使用 @property 声明对象类型的属性时,默认是 strong。实例变量如果不指定修饰符,在 ARC 下默认也是 strong
  • weak: 弱引用。不持有对象,不增加引用计数。当对象不再被任何强引用指向而销毁时,所有指向该对象的弱引用会自动设置为 nil。常用于解决循环引用问题(例如,委托 delegate 模式中,委托方对被委托方的引用通常是弱引用)。
  • assign: 用于基本数据类型或 C 结构体。不涉及引用计数。对于对象类型,使用 assign 会导致野指针问题(对象销毁后指针不会清零)。
  • copy: 创建对象的副本并持有。常用语不可变对象(如 NSString),防止原始对象被修改影响当前对象的副本。

5.2 避免循环引用 (Retain Cycle)

循环引用是 ARC 下最常见的内存泄漏问题。当对象 A 持有对象 B 的强引用,同时对象 B 也持有对象 A 的强引用时,它们会互相“锁住”,即使外部不再有对 A 或 B 的引用,它们的引用计数也永远不会变为零,导致内存泄漏。

解决循环引用的典型方法是使用 weakassign(对于委托对象通常是 weak)打破循环。

“`objectivec
// Example of a potential retain cycle
@interface ClassA : NSObject
@property (nonatomic, strong) ClassB *b;
@end

@interface ClassB : NSObject
// If this were strong, it would create a retain cycle with ClassA’s strong reference to ClassB
@property (nonatomic, weak) ClassA *a; // Use weak to break the cycle
@end
“`

5.3 @autoreleasepool

@autoreleasepool 块用于管理“自动释放”的对象。在非 ARC 时代,一些对象会被标记为自动释放,它们不是立即释放,而是放入当前线程的自动释放池中,等待池子销毁时才释放。

在 ARC 下,编译器会自动管理大多数对象的释放,但在某些场景(如循环中创建大量临时对象)使用 @autoreleasepool 可以及时释放内存,优化性能。在命令行程序的 main 函数中,最外层通常会有一个 @autoreleasepool 块。

objectivec
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Code within this block
// Any objects subject to autorelease within this block will be released when the block exits.
}
return 0;
}

对于初学者,理解 ARC 的 strongweak 关键字以及循环引用的概念是内存管理的重点。

第六章:常用 Foundation 框架

Foundation 框架是 Objective-C 开发的基础,它提供了许多核心的基础类和数据类型,如字符串、数组、字典、数字、日期等。几乎所有的 Objective-C 程序都会用到 Foundation。

6.1 导入 Foundation 框架

通常在 .h 文件或 .m 文件的开头使用 #import <Foundation/Foundation.h> 导入整个框架。#import#include 的升级版,可以防止头文件被重复包含。

6.2 字符串 (NSString 和 NSMutableString)

Objective-C 中的字符串是对象,而不是 C 语言中的字符数组。

  • NSString: 不可变字符串。一旦创建,内容就不能改变。
  • NSMutableString: 可变字符串。创建后可以修改内容(追加、插入、删除等)。

字符串字面量使用 @ 符号前缀:@"Hello, World!"

“`objectivec
// NSString (不可变)
NSString greeting = @”Hello”;
NSString
name = @”Alice”;
NSString *message = [greeting stringByAppendingString:@” “]; // 拼接字符串,返回新字符串
message = [message stringByAppendingString:name];
NSLog(@”%@”, message); // 输出 “Hello Alice”

// 格式化字符串
NSString *formattedString = [NSString stringWithFormat:@”My name is %@ and I am %d.”, name, 25];
NSLog(@”%@”, formattedString); // 输出 “My name is Alice and I am 25.”

// 字符串长度
NSUInteger length = [message length]; // length 是一个无符号整型

// 字符串比较
if ([greeting isEqualToString:@”Hello”]) { // 使用 isEqualToString 进行内容比较
NSLog(@”Greeting is Hello”);
}

// NSMutableString (可变)
NSMutableString *mutableString = [NSMutableString stringWithString:@”Initial string”];
[mutableString appendString:@” added part.”]; // 追加
[mutableString insertString:@”inserted ” atIndex:8]; // 插入
NSLog(@”%@”, mutableString); // 输出 “Initial inserted string added part.”
“`

6.3 数组 (NSArray 和 NSMutableArray)

数组是对象的有序集合。

  • NSArray: 不可变数组。一旦创建,元素就不能改变(不能添加、删除、修改元素)。
  • NSMutableArray: 可变数组。可以添加、删除、修改元素。

创建不可变数组的常用方法:

“`objectivec
NSArray fruits = @[@”Apple”, @”Banana”, @”Cherry”]; // 数组字面量语法 (现代语法)
// NSArray
fruits = [NSArray arrayWithObjects:@”Apple”, @”Banana”, @”Cherry”, nil]; // 传统语法,nil 结束

// 访问元素 (索引从 0 开始)
NSString firstFruit = fruits[0]; // 下标语法 (现代语法)
// NSString
firstFruit = [fruits objectAtIndex:0]; // 传统语法
NSLog(@”First fruit: %@”, firstFruit); // 输出 “First fruit: Apple”

// 数组长度
NSUInteger count = [fruits count]; // count 是一个无符号整型

// 遍历数组
for (NSString *fruit in fruits) { // Fast Enumeration (快速枚举)
NSLog(@”Fruit: %@”, fruit);
}
“`

创建可变数组:

objectivec
NSMutableArray *shoppingList = [NSMutableArray array]; // 创建空的可变数组
[shoppingList addObject:@"Milk"]; // 添加元素
[shoppingList addObject:@"Bread"];
[shoppingList insertObject:@"Eggs" atIndex:0]; // 在指定索引插入
[shoppingList removeObjectAtIndex:1]; // 删除指定索引的元素 ("Milk" 被删除)
NSLog(@"Shopping List: %@", shoppingList); // 输出 "Shopping List: ( Eggs, Bread )"

6.4 字典 (NSDictionary 和 NSMutableDictionary)

字典是键值对的无序集合。每个元素都有一个唯一的键 (Key) 和一个对应的值 (Value)。键通常是字符串,值可以是任何对象。

  • NSDictionary: 不可变字典。
  • NSMutableDictionary: 可变字典。

创建不可变字典的常用方法:

“`objectivec
NSDictionary personInfo = @{@”name”: @”Alice”, @”age”: @25, @”city”: @”New York”}; // 字典字面量语法 (现代语法)
// @25 是将基本数据类型 int 包装成 NSNumber 对象,因为字典只能存储对象。
// NSDictionary
personInfo = [NSDictionary dictionaryWithObjectsAndKeys:
// @”Alice”, @”name”,
// @25, @”age”,
// @”New York”, @”city”,
// nil]; // 传统语法,值在前,键在后,nil 结束

// 访问值 (通过键)
NSString name = personInfo[@”name”]; // 下标语法 (现代语法)
// NSString
name = [personInfo objectForKey:@”name”]; // 传统语法
int age = [personInfo[@”age”] intValue]; // 从 NSNumber 对象获取 int 值
NSLog(@”Name: %@, Age: %d”, name, age); // 输出 “Name: Alice, Age: 25”

// 字典大小 (键值对数量)
NSUInteger count = [personInfo count];
“`

创建可变字典:

objectivec
NSMutableDictionary *settings = [NSMutableDictionary dictionary]; // 创建空的可变字典
[settings setObject:@YES forKey:@"darkMode"]; // 添加键值对
[settings setObject:@NO forKey:@"notifications"];
[settings removeObjectForKey:@"darkMode"]; // 删除键值对
[settings setValue:@"English" forKey:@"language"]; // setValue:forKey: 也可以添加/修改键值对
NSLog(@"Settings: %@", settings); // 输出类似 "{ language = English; notifications = 0; }" (顺序可能不同)

6.5 NSNumber

NSNumber 用于将基本数据类型(如 int, float, BOOL, char 等)封装成对象,以便它们可以存储在集合类(如 NSArray, NSDictionary)中。

“`objectivec
int intValue = 100;
NSNumber *number = @(intValue); // 字面量语法,将 int 封装成 NSNumber

BOOL boolValue = YES;
NSNumber *boolean = @(boolValue); // 将 BOOL 封装成 NSNumber

float floatValue = 3.14f;
NSNumber *floatNumber = @(floatValue); // 将 float 封装成 NSNumber

// 从 NSNumber 对象获取基本类型值
int retrievedInt = [number intValue];
BOOL retrievedBool = [boolean boolValue];
float retrievedFloat = [floatNumber floatValue];

NSLog(@”Int: %d, Bool: %d, Float: %f”, retrievedInt, retrievedBool, retrievedFloat);
“`

第七章:其他重要概念

学习 Objective-C,还有一些其他重要概念需要了解。

7.1 协议 (Protocols)

协议定义了一组方法,类可以声明遵循 (conform) 某个协议,并实现协议中声明的方法。协议类似于其他语言中的接口 (Interface)。

“`objectivec
// MyProtocol.h 文件

import

// @protocol 声明协议
@protocol MyProtocol // 协议可以继承其他协议,通常继承 NSObject 协议

// @required: 遵循协议的类必须实现的方法 (默认)
-(void)requiredMethod;

// @optional: 遵循协议的类可以选择实现的方法
@optional
-(void)optionalMethod;

@end
“`

“`objectivec
// MyClass.h 文件

import

import “MyProtocol.h” // 引入协议头文件

// MyClass 遵循 MyProtocol 协议
@interface MyClass : NSObject

@end
“`

“`objectivec
// MyClass.m 文件

import “MyClass.h”

@implementation MyClass

// 实现协议中 required 的方法
-(void)requiredMethod {
NSLog(@”Implementing the required method.”);
}

// 实现协议中 optional 的方法 (可选)
-(void)optionalMethod {
NSLog(@”Implementing the optional method.”);
}

@end
“`

协议常用于委托 (Delegate) 模式,一个对象(委托方)将某些任务委托给另一个对象(被委托方,遵循特定协议)。

7.2 分类 (Categories)

分类是一种强大的特性,允许你为现有类(包括 Foundation 和 UIKit 等框架中的类)添加方法,而无需创建子类。这对于为现有类添加一些实用的工具方法非常有用。

“`objectivec
// NSString+MyCategory.h 文件
// 分类名是加在类名后面的括号里

import

@interface NSString (MyCategory) // 为 NSString 类添加分类 MyCategory

// 添加一个方法来反转字符串
-(NSString *)reversedString;

@end
“`

“`objectivec
// NSString+MyCategory.m 文件

import “NSString+MyCategory.h”

@implementation NSString (MyCategory)

-(NSString )reversedString {
NSUInteger len = [self length];
NSMutableString
reversed = [NSMutableString stringWithCapacity:len];
for (NSInteger i = len – 1; i >= 0; i–) {
[reversed appendFormat:@”%C”, [self characterAtIndex:i]]; // %C 用于输出 Unicode 字符
}
return reversed;
}

@end
“`

使用分类添加的方法:

“`objectivec
// 在 main.m 中

import

import “NSString+MyCategory.h” // 引入分类头文件

int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString original = @”Hello”;
NSString
reversed = [original reversedString]; // 调用分类添加的方法
NSLog(@”Original: %@, Reversed: %@”, original, reversed); // 输出 “Original: Hello, Reversed: olleH”
}
return 0;
}
“`

7.3 块 (Blocks)

块是 Objective-C 中对闭包 (Closure) 的实现。它可以捕获其定义范围内的变量,并可以在之后执行。块在处理异步操作、集合遍历、动画等方面非常有用。

块的语法:返回值类型 (^块名称)(参数列表)

“`objectivec
// 定义一个块变量
double (^multiplyBlock)(double, double) =
^(double num1, double num2) { // ^ 表示这是一个块,(参数列表) {实现代码}
return num1 * num2;
};

// 调用块
double result = multiplyBlock(5.0, 3.0);
NSLog(@”Result: %f”, result); // 输出 “Result: 15.000000”

// 块捕获变量 (默认捕获的是常量,如果需要修改外部变量,需要使用 __block 修饰)
__block int counter = 0;
void (^incrementCounter)(void) = ^{
counter++; // 可以修改 __block 变量
NSLog(@”Counter: %d”, counter);
};

incrementCounter(); // 输出 “Counter: 1”
incrementCounter(); // 输出 “Counter: 2”
“`

第八章:使用 Xcode 调试代码

编写代码只是第一步,调试是发现和修复错误的重要环节。Xcode 提供了强大的调试工具。

8.1 设置断点

断点是程序执行过程中暂停的点。点击代码行号的左侧区域即可设置或取消断点。程序运行到断点处会自动暂停。

8.2 运行和调试

点击 Xcode 窗口左上角的“运行”按钮(或使用快捷键 Cmd + R)可以运行程序。如果设置了断点,点击旁边的“调试”按钮(或使用快捷键 Cmd + I)可以在调试模式下运行。

8.3 调试界面

当程序在断点处暂停时,Xcode 会切换到调试界面。主要区域包括:

  • 调试控制台: 显示程序输出 (NSLog 的内容) 和调试信息。
  • 变量视图: 显示当前作用域内的变量及其值。你可以检查、修改变量的值。
  • 线程视图: 显示当前程序的线程信息。
  • 堆栈视图: 显示当前执行的函数调用堆栈,可以查看程序是如何执行到当前位置的。

8.4 调试操作

在程序暂停时,可以使用调试控制条进行以下操作:

  • 继续执行 (Continue): 继续运行程序直到下一个断点或程序结束。
  • 步过 (Step Over): 执行当前行代码,如果当前行是一个函数调用,则整体执行完函数,不进入函数内部。
  • 步入 (Step Into): 执行当前行代码,如果当前行是一个函数调用,则进入函数内部的第一行。
  • 步出 (Step Out): 从当前函数内部跳出,执行完当前函数剩余的代码,回到调用该函数的地方。

熟练使用断点和调试工具是提高开发效率的关键。

第九章:下一步:构建应用与学习 Swift

恭喜你!通过上述章节的学习,你已经掌握了 Objective-C 的基本语法、面向对象特性、内存管理以及 Foundation 框架的常用类。这为你进一步深入苹果开发奠定了基础。

9.1 学习 UI 开发 (iOS/macOS)

如果你希望开发带有用户界面的应用,下一步就是学习对应的 UI 框架:

  • iOS 开发: 学习 UIKit 框架。它提供了构建 iOS 应用用户界面的各种组件(按钮、标签、列表、视图控制器等)。
  • macOS 开发: 学习 AppKit 框架。它提供了构建 macOS 应用用户界面的组件。

学习 UI 框架通常会涉及界面布局(Auto Layout)、事件处理、数据展示(UITableView/NSTableView, UICollectionView/NSCollectionView)等内容。

9.2 深入苹果框架

苹果提供了大量的框架来支持各种功能,例如:

  • Core Data (数据持久化)
  • Core Animation (动画)
  • Core Graphics (绘图)
  • Grand Central Dispatch (GCD) 和 NSOperation (多线程)
  • Networking (网络编程)

等等。根据你的项目需求,逐步学习和掌握这些框架将使你能够开发更复杂、功能更丰富的应用。

9.3 学习 Swift

尽管你学习了 Objective-C,但 Swift 是苹果未来的方向。掌握 Objective-C 后再学习 Swift 会更加容易,因为许多概念(如类、对象、属性、方法、协议、内存管理等)以及苹果的框架设计思想是共通的。Swift 拥有更现代的语法、更好的安全性和性能。学习 Swift 将是你成为一名全面苹果开发者的必经之路。

9.4 实践出真知

最重要的是动手实践。从小项目开始,例如:

  • 一个简单的命令行工具,处理文件或字符串。
  • 一个基础的计算器应用。
  • 一个简单的待办事项列表应用。

在实践中你会遇到问题,解决问题的过程本身就是最好的学习。多阅读官方文档、查阅 Stack Overflow、参与开发者社区。

结论:坚持与探索

学习 Objective-C 是一个需要耐心和实践的过程。你已经迈出了重要的第一步,掌握了它的基础。Objective-C 独特的语法和动态性可能会让你感到新奇,但一旦理解了其核心思想(尤其是消息传递),你将能更好地理解苹果的开发生态。

请记住,编程是一项技能,需要不断练习和探索。不要害怕犯错,每一次错误都是一次学习的机会。祝你在 Objective-C 的学习旅程中取得成功,并期待你用所学知识创造出令人惊叹的应用!


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部