Objective-C 基础语法全面解析
一、Objective-C 简介
Objective-C 是一种通用、高级、面向对象的编程语言,它扩展了标准的 ANSI C 语言,加入了面向对象编程的特性。它由 Brad Cox 在 20 世纪 80 年代初开发,最初用于 NeXTSTEP 操作系统,后来随着苹果公司收购 NeXT,Objective-C 成为了 macOS 和 iOS 开发的主要编程语言。
Objective-C 语言的设计理念融合了 C 语言的高效性和 Smalltalk 的面向对象特性,其最大的特点就是动态绑定和运行时特性,这使得开发者在编程时可以更加灵活地控制程序的行为。
二、数据类型
2.1 基本数据类型
Objective-C 支持 C 语言的所有基本数据类型,包括整型、浮点型、字符型和布尔型。
整型:
整型用于表示整数,常见的有 char
、short
、int
和 long
。在 64 位系统中,int
通常为 32 位,long
为 64 位。例如:
int age = 25;
short num = 100;
浮点型:
浮点型用于表示带有小数部分的数字,有 float
(单精度)和 double
(双精度)。float
一般占用 4 字节,double
占用 8 字节。例如:
float piFloat = 3.14159f;
double piDouble = 3.141592653589793;
注意,在表示 float
类型常量时,需要在数字后面加上 f
或 F
。
字符型:
字符型用于表示单个字符,使用 char
类型。字符需要用单引号括起来。例如:
char grade = 'A';
布尔型:
Objective-C 中的布尔型用 BOOL
表示,它实际上是一个 signed char
类型,取值为 YES
(1)或 NO
(0)。例如:
BOOL isDone = YES;
2.2 枚举类型
枚举类型是一种用户自定义的数据类型,它允许定义一组命名的整型常量。使用 enum
关键字来定义枚举类型。例如:
enum Weekday {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
默认情况下,Monday
的值为 0,后续的值依次递增 1。也可以手动指定值:
enum Weekday {
Monday = 1,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
使用枚举类型定义变量:
enum Weekday today = Wednesday;
2.3 结构体和联合体
结构体:
结构体是一种可以将不同类型的数据组合在一起的自定义数据类型。使用 struct
关键字定义。例如:
struct Point {
int x;
int y;
};
定义结构体变量:
struct Point p1;
p1.x = 10;
p1.y = 20;
也可以在定义结构体时同时定义变量:
struct Point {
int x;
int y;
} p2;
p2.x = 30;
p2.y = 40;
联合体:
联合体也是一种自定义数据类型,它允许不同类型的数据共享相同的内存空间。使用 union
关键字定义。例如:
union Data {
int i;
float f;
char c;
};
定义联合体变量:
union Data d;
d.i = 10;
由于联合体成员共享内存,此时 d.f
和 d.c
的值也会受到影响,因为它们和 d.i
占用同一块内存。
三、变量与常量
3.1 变量
变量是程序中用于存储数据的容器,其值在程序运行过程中可以改变。在 Objective-C 中,变量必须先声明后使用。例如:
int number;
number = 5;
变量的作用域决定了变量在程序中的可见范围。常见的作用域有局部作用域(在函数内部声明的变量)和全局作用域(在函数外部声明的变量)。例如:
int globalVar = 10; // 全局变量
void myFunction() {
int localVar = 20; // 局部变量
NSLog(@"Local variable: %d", localVar);
NSLog(@"Global variable: %d", globalVar);
}
3.2 常量
常量是在程序运行过程中其值不能改变的量。在 Objective-C 中有两种定义常量的方式:
使用 #define
宏定义:
#define PI 3.14159
这种方式是在预处理阶段进行替换,没有类型检查。
使用 const
关键字:
const float pi = 3.14159f;
使用 const
定义的常量有类型检查,并且作用域遵循变量的作用域规则。
四、运算符
4.1 算术运算符
Objective-C 支持常见的算术运算符,如加(+
)、减(-
)、乘(*
)、除(/
)和取模(%
)。例如:
int a = 10;
int b = 3;
int sum = a + b;
int product = a * b;
int remainder = a % b;
注意,两个整数相除结果为整数,会舍去小数部分。如果要得到精确的结果,至少有一个操作数应为浮点型。
4.2 赋值运算符
赋值运算符用于给变量赋值,最基本的是 =
。还存在复合赋值运算符,如 +=
、-=
、*=
、/=
、%=
等。例如:
int num = 5;
num += 3; // 等价于 num = num + 3;
4.3 比较运算符
比较运算符用于比较两个值,结果为 BOOL
类型。常见的比较运算符有等于(==
)、不等于(!=
)、大于(>
)、小于(<
)、大于等于(>=
)和小于等于(<=
)。例如:
int a = 10;
int b = 20;
BOOL isGreater = a > b;
4.4 逻辑运算符
逻辑运算符用于组合多个条件,结果也是 BOOL
类型。有逻辑与(&&
)、逻辑或(||
)和逻辑非(!
)。例如:
int a = 10;
int b = 20;
BOOL result1 = (a > 5) && (b < 30);
BOOL result2 = (a < 5) || (b > 15);
BOOL result3 =!(a == b);
4.5 位运算符
位运算符用于对二进制位进行操作,包括按位与(&
)、按位或(|
)、按位异或(^
)、按位取反(~
)、左移(<<
)和右移(>>
)。例如:
int a = 5; // 二进制为 00000101
int b = 3; // 二进制为 00000011
int andResult = a & b; // 按位与,结果为 00000001,即 1
int orResult = a | b; // 按位或,结果为 00000111,即 7
五、流程控制语句
5.1 if - else 语句
if - else
语句用于根据条件执行不同的代码块。例如:
int age = 18;
if (age >= 18) {
NSLog(@"You are an adult.");
} else {
NSLog(@"You are a minor.");
}
也可以有多个 if - else
嵌套,形成 if - else if - else
结构:
int score = 85;
if (score >= 90) {
NSLog(@"Grade: A");
} else if (score >= 80) {
NSLog(@"Grade: B");
} else if (score >= 70) {
NSLog(@"Grade: C");
} else {
NSLog(@"Grade: D");
}
5.2 switch - case 语句
switch - case
语句用于根据一个整型或字符型表达式的值来选择执行不同的分支。例如:
int day = 3;
switch (day) {
case 1:
NSLog(@"Monday");
break;
case 2:
NSLog(@"Tuesday");
break;
case 3:
NSLog(@"Wednesday");
break;
default:
NSLog(@"Other day");
break;
}
注意,每个 case
分支结尾一般需要使用 break
语句,否则会继续执行下一个 case
分支的代码。
5.3 for 循环
for
循环用于重复执行一段代码,它有初始化、条件判断和更新三个部分。例如:
for (int i = 0; i < 5; i++) {
NSLog(@"Count: %d", i);
}
在上述代码中,int i = 0
是初始化部分,i < 5
是条件判断部分,i++
是更新部分。
5.4 while 循环
while
循环先判断条件,只要条件为真就会重复执行循环体。例如:
int num = 0;
while (num < 5) {
NSLog(@"Number: %d", num);
num++;
}
5.5 do - while 循环
do - while
循环先执行一次循环体,然后再判断条件。因此,无论条件是否成立,循环体至少会执行一次。例如:
int value = 10;
do {
NSLog(@"Value: %d", value);
value -= 2;
} while (value > 0);
六、函数
6.1 函数的定义与声明
函数是一段完成特定任务的代码块。在 Objective-C 中,函数需要先声明后使用。函数声明包括函数名、参数列表和返回类型,函数定义则包含函数体。例如:
// 函数声明
int add(int a, int b);
// 函数定义
int add(int a, int b) {
return a + b;
}
在 main
函数中调用 add
函数:
int main() {
int result = add(3, 5);
NSLog(@"The result is: %d", result);
return 0;
}
6.2 函数参数与返回值
函数可以有零个或多个参数,参数就像函数内部的局部变量,在函数被调用时接收传递进来的值。函数也可以有返回值,通过 return
语句返回。例如,一个计算两个数平均值的函数:
float average(float num1, float num2) {
return (num1 + num2) / 2;
}
调用该函数:
float avg = average(10.5f, 15.5f);
NSLog(@"The average is: %f", avg);
6.3 函数的递归
递归是指函数在其函数体内调用自身的过程。例如,计算阶乘的递归函数:
int factorial(int n) {
if (n == 0 || n == 1) {
return 1;
} else {
return n * factorial(n - 1);
}
}
调用该函数计算 5 的阶乘:
int result = factorial(5);
NSLog(@"5! is: %d", result);
七、数组
7.1 一维数组
一维数组是相同类型数据的有序集合。在 Objective-C 中,数组的声明和初始化方式如下:
int numbers[5]; // 声明一个包含 5 个整数的数组
numbers[0] = 10;
numbers[1] = 20;
numbers[2] = 30;
numbers[3] = 40;
numbers[4] = 50;
// 或者在声明时初始化
int scores[] = {85, 90, 95, 100};
访问数组元素通过下标,下标从 0 开始。例如:
int firstScore = scores[0];
7.2 二维数组
二维数组可以看作是数组的数组,常用于表示矩阵等数据结构。例如:
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
访问二维数组元素:
int element = matrix[1][2]; // 访问第二行第三列的元素,值为 6
7.3 数组与函数
数组可以作为函数的参数传递。当数组作为参数传递时,实际上传递的是数组的首地址。例如:
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
NSLog(@"%d", arr[i]);
}
}
调用该函数:
int numbers[] = {1, 2, 3, 4, 5};
printArray(numbers, 5);
八、指针
8.1 指针的基本概念
指针是一个变量,它存储的是另一个变量的内存地址。在 Objective-C 中,使用 *
来声明指针变量。例如:
int num = 10;
int *ptr; // 声明一个指向 int 类型的指针
ptr = # // 将 num 的地址赋给 ptr
通过指针访问变量的值使用 *
运算符,称为解引用。例如:
NSLog(@"Value of num through pointer: %d", *ptr);
8.2 指针与数组
数组名实际上是数组首元素的地址,因此可以将数组名赋值给指针。例如:
int numbers[5] = {1, 2, 3, 4, 5};
int *ptr = numbers;
通过指针访问数组元素:
NSLog(@"First element: %d", *ptr);
NSLog(@"Second element: %d", *(ptr + 1));
8.3 指针与函数
指针可以作为函数的参数传递,这样可以在函数内部修改调用函数中的变量值。例如:
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
调用该函数:
int x = 5;
int y = 10;
swap(&x, &y);
NSLog(@"x: %d, y: %d", x, y);
九、面向对象编程基础
9.1 类与对象
在 Objective-C 中,类是对象的蓝图,它定义了对象的属性(成员变量)和行为(方法)。使用 @interface
和 @implementation
来定义类。例如:
// 定义一个 Person 类
@interface Person : NSObject {
NSString *name;
int age;
}
- (void)setName:(NSString *)newName;
- (NSString *)name;
- (void)setAge:(int)newAge;
- (int)age;
- (void)introduce;
@end
@implementation Person
- (void)setName:(NSString *)newName {
name = newName;
}
- (NSString *)name {
return name;
}
- (void)setAge:(int)newAge {
age = newAge;
}
- (int)age {
return age;
}
- (void)introduce {
NSLog(@"Hi, I'm %@ and I'm %d years old.", name, age);
}
@end
创建对象并使用:
int main() {
Person *person = [[Person alloc] init];
[person setName:@"John"];
[person setAge:25];
[person introduce];
[person release];
return 0;
}
在上述代码中,Person
类继承自 NSObject
,这是 Objective-C 中所有类的根类。name
和 age
是成员变量,setName:
、name
、setAge:
、age
和 introduce
是方法。
9.2 封装
封装是将数据和操作数据的方法封装在一起,通过访问控制来保护数据的安全性。在 Objective-C 中,可以使用 @private
、@protected
和 @public
关键字来控制成员变量的访问权限。例如:
@interface Animal : NSObject {
@private
NSString *species;
@protected
int age;
@public
NSString *name;
}
- (void)setSpecies:(NSString *)newSpecies;
- (NSString *)species;
@end
@implementation Animal
- (void)setSpecies:(NSString *)newSpecies {
species = newSpecies;
}
- (NSString *)species {
return species;
}
@end
在上述代码中,species
是私有成员变量,只能在类内部访问;age
是受保护成员变量,可以在类及其子类中访问;name
是公共成员变量,可以在任何地方访问。
9.3 继承
继承是指一个类可以从另一个类中获取属性和方法。子类可以继承父类的特性,并可以添加自己的新特性。例如:
@interface Vehicle : NSObject {
NSString *brand;
int wheels;
}
- (void)setBrand:(NSString *)newBrand;
- (NSString *)brand;
- (void)setWheels:(int)newWheels;
- (int)wheels;
@end
@implementation Vehicle
- (void)setBrand:(NSString *)newBrand {
brand = newBrand;
}
- (NSString *)brand {
return brand;
}
- (void)setWheels:(int)newWheels {
wheels = newWheels;
}
- (int)wheels {
return wheels;
}
@end
@interface Car : Vehicle {
int doors;
}
- (void)setDoors:(int)newDoors;
- (int)doors;
@end
@implementation Car
- (void)setDoors:(int)newDoors {
doors = newDoors;
}
- (int)doors {
return doors;
}
@end
在上述代码中,Car
类继承自 Vehicle
类,Car
类拥有 Vehicle
类的 brand
和 wheels
成员变量以及相关方法,同时还拥有自己的 doors
成员变量和方法。
9.4 多态
多态是指不同对象对同一消息作出不同响应的能力。在 Objective-C 中,通过继承和方法重写来实现多态。例如:
@interface Shape : NSObject
- (void)draw;
@end
@implementation Shape
- (void)draw {
NSLog(@"Drawing a shape.");
}
@end
@interface Circle : Shape
- (void)draw;
@end
@implementation Circle
- (void)draw {
NSLog(@"Drawing a circle.");
}
@end
@interface Rectangle : Shape
- (void)draw;
@end
@implementation Rectangle
- (void)draw {
NSLog(@"Drawing a rectangle.");
}
@end
使用多态:
Shape *shape1 = [[Circle alloc] init];
Shape *shape2 = [[Rectangle alloc] init];
[shape1 draw];
[shape2 draw];
[shape1 release];
[shape2 release];
在上述代码中,Circle
和 Rectangle
类继承自 Shape
类并重写了 draw
方法。通过 Shape
类型的指针调用 draw
方法时,实际调用的是具体对象所属类的 draw
方法,实现了多态。
十、内存管理
10.1 引用计数原理
Objective-C 使用引用计数(Reference Counting)来管理内存。每个对象都有一个引用计数,当对象被创建时,引用计数为 1。每次有新的指针指向该对象时,引用计数加 1;当指针不再指向该对象时,引用计数减 1。当引用计数为 0 时,对象的内存被释放。例如:
NSString *str1 = [[NSString alloc] initWithString:@"Hello"]; // str1 引用计数为 1
NSString *str2 = str1; // str2 引用计数加 1,此时 str1 和 str2 引用计数都为 2
[str1 release]; // str1 引用计数减 1,此时 str1 引用计数为 1,str2 引用计数仍为 2
[str2 release]; // str2 引用计数减 1,此时引用计数为 0,对象内存被释放
10.2 自动释放池
自动释放池(Autorelease Pool)是一种内存管理机制,它可以延迟对象的释放。当一个对象发送 autorelease
消息时,它会被添加到最近的自动释放池中。当自动释放池被销毁时,池中的所有对象都会收到 release
消息。例如:
@autoreleasepool {
NSString *str = [[NSString alloc] initWithString:@"World"];
[str autorelease];
// 自动释放池销毁时,str 会收到 release 消息
}
10.3 ARC(自动引用计数)
ARC 是 Xcode 4.2 引入的一种自动内存管理机制,它大大简化了内存管理的工作。在 ARC 模式下,编译器会自动在适当的位置插入 retain
、release
和 autorelease
等内存管理代码。例如:
NSString *str = @"Hello ARC";
// 在 ARC 模式下,不需要手动管理 str 的内存
虽然 ARC 方便了开发,但开发者仍需要了解内存管理的基本原理,以便在处理循环引用等复杂情况时能够正确处理内存问题。
十一、类别与协议
11.1 类别(Category)
类别允许在不子类化的情况下向现有类添加方法。使用 @interface
和 @implementation
定义类别,类别名放在类名后面的括号中。例如:
@interface NSString (MyCategory)
- (NSString *)reverseString;
@end
@implementation NSString (MyCategory)
- (NSString *)reverseString {
NSMutableString *reversed = [NSMutableString stringWithCapacity:[self length]];
NSInteger length = [self length];
for (NSInteger i = length - 1; i >= 0; i--) {
[reversed appendFormat:@"%C", [self characterAtIndex:i]];
}
return reversed;
}
@end
使用类别方法:
NSString *original = @"Hello";
NSString *reversed = [original reverseString];
NSLog(@"Reversed string: %@", reversed);
11.2 协议(Protocol)
协议定义了一组方法的声明,但不提供实现。任何类都可以声明遵守某个协议,并实现协议中的方法。例如:
@protocol Printable <NSObject>
- (void)print;
@end
@interface Document : NSObject <Printable>
@property (nonatomic, copy) NSString *content;
@end
@implementation Document
- (void)print {
NSLog(@"Printing document: %@", self.content);
}
@end
在上述代码中,Document
类声明遵守 Printable
协议,并实现了 print
方法。
通过以上对 Objective-C 基础语法的全面解析,希望读者能够对这门编程语言有更深入的理解,为进一步的 iOS 和 macOS 开发打下坚实的基础。在实际开发中,还需要不断实践和探索,以掌握更多高级特性和优化技巧。