学习 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 非常简单:
- 打开 Mac App Store: 在你的 Mac 上找到并打开 App Store 应用。
- 搜索 Xcode: 在搜索框中输入 “Xcode”。
- 下载并安装: 找到 Xcode 应用,点击 “获取”,然后点击 “安装”。Xcode 的体积较大,下载和安装可能需要一些时间,取决于你的网络速度。
- 启动 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 语言本身,我们先创建一个不涉及图形界面的命令行工具:
- 在项目模板选择界面,选择 macOS -> Command Line Tool。点击 Next。
- Product Name: 输入项目名称,例如 “ObjectiveCPrimer”。
- Organization Identifier: 输入一个反向域名格式的组织标识符,例如 “com.yourcompany” (用你自己的公司或名字替换 yourcompany)。这将构成你的 Bundle Identifier。
- Language: 确保选择了 “Objective-C”。
- Use Core Data: 不勾选。
- Include Unit Tests: 不勾选。
- Include UI Tests: 不勾选。
- 点击 Next。
- 选择一个文件夹来保存你的项目,点击 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 语言提供了各种运算符:
- 算术运算符:
+
(加),-
(减),*
(乘),/
(除),%
(取模) - 关系运算符:
==
(等于),!=
(不等于),>
(大于),<
(小于),>=
(大于等于),<=
(小于等于)。这些运算符的结果是布尔值 (YES
或NO
)。 - 逻辑运算符:
&&
(逻辑与),||
(逻辑或),!
(逻辑非)。 - 赋值运算符:
=
(赋值),+=
,-=
,*=
等复合赋值运算符。 - 自增/自减运算符:
++
(自增 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 的内存管理经历了几个阶段:
- 手动引用计数 (Manual Retain-Release / MRR): 需要开发者手动调用
retain
(增加引用计数)、release
(减少引用计数)、autorelease
(放入自动释放池)。这是 Objective-C 早期的主要内存管理方式。 - 垃圾回收 (Garbage Collection): 在 macOS 上短暂出现,但在 iOS 上从未支持,并且已经被废弃。
- 自动引用计数 (Automatic Reference Counting / ARC): 在 iOS 5 和 macOS 10.7 引入,是目前主流的内存管理方式。编译器在编译时自动插入
retain
、release
、autorelease
代码。开发者无需手动管理引用计数,但仍需理解其原理。
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 的引用,它们的引用计数也永远不会变为零,导致内存泄漏。
解决循环引用的典型方法是使用 weak
或 assign
(对于委托对象通常是 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 的 strong
和 weak
关键字以及循环引用的概念是内存管理的重点。
第六章:常用 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
// @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 的学习旅程中取得成功,并期待你用所学知识创造出令人惊叹的应用!