Objective-C基础语法与核心概念解析
一、Objective-C 概述
Objective-C 是一种通用、高级、面向对象的编程语言,它是在 C 语言的基础上添加了面向对象的特性而形成的。Objective-C 是 macOS 和 iOS 开发的主要语言,许多知名的应用程序,如 Safari、iTunes 等都是使用 Objective-C 编写的。它采用动态运行时系统,这意味着很多面向对象的特性,如方法调用,是在运行时而不是编译时确定的。
二、基础语法
2.1 数据类型
Objective-C 基于 C 语言,所以它支持 C 语言的基本数据类型,如整型(int
)、浮点型(float
、double
)、字符型(char
)和布尔型(BOOL
,实际上是 signed char
,YES
对应 1,NO
对应 0)。
// 整型变量声明
int age = 25;
// 浮点型变量声明
float height = 175.5f;
double weight = 70.0;
// 字符型变量声明
char grade = 'A';
// 布尔型变量声明
BOOL isStudent = YES;
此外,Objective-C 还引入了对象类型,对象类型使用指针表示,最基本的对象类型是 id
,它可以指向任何 Objective-C 对象。
id someObject;
2.2 变量与常量
变量在使用前需要声明,声明包括变量类型和变量名。变量可以在声明时初始化,也可以后续赋值。
int number;
number = 10;
// 声明并初始化
float price = 9.99f;
常量在声明时必须初始化,并且其值在程序运行过程中不能改变。在 Objective-C 中,可以使用 #define
预处理指令定义常量,也可以使用 const
关键字。
// 使用 #define 定义常量
#define PI 3.141592653589793
// 使用 const 关键字定义常量
const float gravity = 9.81f;
2.3 运算符
Objective-C 支持 C 语言的所有运算符,包括算术运算符(+
、-
、*
、/
)、赋值运算符(=
、+=
、-=
等)、比较运算符(==
、!=
、>
、<
等)、逻辑运算符(&&
、||
、!
)、位运算符(&
、|
、^
、~
、<<
、>>
)等。
int a = 5;
int b = 3;
int sum = a + b;
BOOL isGreater = a > b;
2.4 控制语句
Objective-C 提供了常见的控制语句,如 if - else
、switch - case
、for
、while
、do - while
等。
if - else 语句
int score = 85;
if (score >= 90) {
NSLog(@"A 等级");
} else if (score >= 80) {
NSLog(@"B 等级");
} else {
NSLog(@"其他等级");
}
switch - case 语句
int day = 3;
switch (day) {
case 1:
NSLog(@"星期一");
break;
case 2:
NSLog(@"星期二");
break;
case 3:
NSLog(@"星期三");
break;
default:
NSLog(@"其他日期");
break;
}
for 循环
for (int i = 0; i < 5; i++) {
NSLog(@"当前值: %d", i);
}
while 循环
int count = 0;
while (count < 3) {
NSLog(@"计数: %d", count);
count++;
}
do - while 循环
int num = 0;
do {
NSLog(@"数值: %d", num);
num++;
} while (num < 3);
三、面向对象编程
3.1 类与对象
在 Objective-C 中,类是对象的蓝图,定义了对象的属性(成员变量)和行为(方法)。类的定义包括接口(.h
文件)和实现(.m
文件)两部分。
接口部分(.h
文件)
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
- (void)introduce;
@end
在上述代码中,@interface
关键字开始定义一个类,类名是 Person
,它继承自 NSObject
类(Objective-C 中所有类的根类)。@property
声明了两个属性,name
和 age
,nonatomic
表示非原子性访问,copy
用于字符串属性的拷贝,assign
用于基本数据类型属性的赋值。- (void)introduce;
声明了一个实例方法。
实现部分(.m
文件)
#import "Person.h"
@implementation Person
- (void)introduce {
NSLog(@"我叫 %@,今年 %ld 岁。", self.name, (long)self.age);
}
@end
@implementation
关键字开始类的实现,在这个部分实现了 introduce
方法。
创建对象并使用:
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
person.name = @"张三";
person.age = 30;
[person introduce];
}
return 0;
}
在 main
函数中,首先使用 alloc
分配内存,然后使用 init
初始化对象,接着设置对象的属性并调用 introduce
方法。
3.2 继承
继承是面向对象编程的重要特性之一,它允许一个类从另一个类继承属性和方法。子类可以重写父类的方法,也可以添加新的属性和方法。
// Student.h
#import <Foundation/Foundation.h>
#import "Person.h"
@interface Student : Person
@property (nonatomic, copy) NSString *school;
- (void)study;
@end
// Student.m
#import "Student.h"
@implementation Student
- (void)study {
NSLog(@"%@ 在 %@ 学习。", self.name, self.school);
}
@end
// main.m
#import <Foundation/Foundation.h>
#import "Student.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Student *student = [[Student alloc] init];
student.name = @"李四";
student.age = 20;
student.school = @"北京大学";
[student introduce];
[student study];
}
return 0;
}
在上述代码中,Student
类继承自 Person
类,它不仅拥有 Person
类的 name
和 age
属性以及 introduce
方法,还添加了 school
属性和 study
方法。
3.3 多态
多态是指同一个方法调用,根据对象类型的不同,会有不同的行为。在 Objective-C 中,多态主要通过继承和方法重写来实现。
假设我们有一个父类 Animal
和两个子类 Dog
和 Cat
,Animal
类有一个 makeSound
方法,Dog
和 Cat
类重写这个方法。
// Animal.h
#import <Foundation/Foundation.h>
@interface Animal : NSObject
- (void)makeSound;
@end
// Animal.m
#import "Animal.h"
@implementation Animal
- (void)makeSound {
NSLog(@"动物发出声音");
}
@end
// Dog.h
#import <Foundation/Foundation.h>
#import "Animal.h"
@interface Dog : Animal
@end
// Dog.m
#import "Dog.h"
@implementation Dog
- (void)makeSound {
NSLog(@"汪汪汪");
}
@end
// Cat.h
#import <Foundation/Foundation.h>
#import "Animal.h"
@interface Cat : Animal
@end
// Cat.m
#import "Cat.h"
@implementation Cat
- (void)makeSound {
NSLog(@"喵喵喵");
}
@end
// main.m
#import <Foundation/Foundation.h>
#import "Animal.h"
#import "Dog.h"
#import "Cat.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Animal *animal1 = [[Dog alloc] init];
Animal *animal2 = [[Cat alloc] init];
[animal1 makeSound];
[animal2 makeSound];
}
return 0;
}
在 main
函数中,虽然 animal1
和 animal2
都被声明为 Animal
类型,但实际上它们分别指向 Dog
和 Cat
对象,调用 makeSound
方法时会执行各自类中重写的方法,体现了多态性。
四、内存管理
4.1 引用计数原理
Objective-C 使用引用计数(Reference Counting)来管理内存。每个对象都有一个引用计数,当对象被创建时,引用计数为 1。当有新的变量指向该对象时,引用计数加 1;当指向对象的变量不再使用该对象(如变量超出作用域或被赋值为 nil
)时,引用计数减 1。当引用计数为 0 时,对象所占用的内存被释放。
4.2 手动内存管理
在手动内存管理模式下,开发人员需要手动调用 retain
、release
和 autorelease
方法来管理对象的引用计数。
// 创建对象,引用计数为 1
NSString *string1 = [[NSString alloc] initWithString:@"Hello"];
// 手动增加引用计数
[string1 retain];
// 手动减少引用计数
[string1 release];
// 使用 autorelease,将对象放入自动释放池,在自动释放池销毁时减少引用计数
NSString *string2 = [[[NSString alloc] initWithString:@"World"] autorelease];
4.3 自动引用计数(ARC)
ARC 是 Xcode 4.2 引入的一项功能,它自动管理对象的内存,开发人员无需手动调用 retain
、release
和 autorelease
方法。ARC 会在编译时自动插入合适的内存管理代码。
// ARC 模式下,无需手动管理内存
NSString *message = @"Hello, ARC!";
五、消息传递
5.1 动态方法解析
在 Objective-C 中,方法调用实际上是向对象发送消息。当一个对象接收到一个消息时,它会在运行时查找对应的方法实现。如果对象在其方法列表中找不到该方法,会进入动态方法解析阶段。
@interface MyClass : NSObject
- (void)unknownMethod;
@end
@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(unknownMethod)) {
class_addMethod(self, sel, (IMP)customMethod, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void customMethod(id self, SEL _cmd) {
NSLog(@"动态添加的方法被调用");
}
@end
在上述代码中,MyClass
类在接收到 unknownMethod
消息且找不到该方法实现时,会尝试通过 resolveInstanceMethod:
方法动态添加方法实现。
5.2 备用接收者
如果动态方法解析没有找到方法实现,对象会尝试寻找备用接收者。即对象会调用 forwardingTargetForSelector:
方法,看是否能返回一个可以处理该消息的对象。
@interface MyClass : NSObject
- (void)unknownMethod;
@end
@interface OtherClass : NSObject
- (void)handleUnknownMethod;
@end
@implementation OtherClass
- (void)handleUnknownMethod {
NSLog(@"OtherClass 处理未知方法");
}
@end
@implementation MyClass
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(unknownMethod)) {
return [[OtherClass alloc] init];
}
return nil;
}
@end
在这个例子中,MyClass
接收到 unknownMethod
消息且未找到直接实现时,会返回 OtherClass
的实例作为备用接收者来处理该消息。
5.3 完整的消息转发
如果备用接收者也没有找到,就会进入完整的消息转发阶段。对象会创建一个 NSInvocation
对象,封装消息的参数和选择器,然后调用 forwardInvocation:
方法进行处理。
@interface MyClass : NSObject
- (void)unknownMethod;
@end
@implementation MyClass
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL sel = anInvocation.selector;
if ([self respondsToSelector:sel]) {
[anInvocation invokeWithTarget:self];
} else {
NSLog(@"无法处理的消息: %@", NSStringFromSelector(sel));
}
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(unknownMethod)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
@end
在上述代码中,MyClass
通过 methodSignatureForSelector:
方法获取方法签名,然后在 forwardInvocation:
方法中根据签名来处理消息,如果无法处理则打印提示信息。
六、类别与协议
6.1 类别(Category)
类别可以在不子类化的情况下,为已有的类添加新的方法。类别通常用于将类的实现分散到多个文件中,或者为系统类添加自定义方法。
// NSString+Additions.h
#import <Foundation/Foundation.h>
@interface NSString (Additions)
- (NSString *)reversedString;
@end
// NSString+Additions.m
#import "NSString+Additions.h"
@implementation NSString (Additions)
- (NSString *)reversedString {
NSMutableString *reversed = [NSMutableString stringWithCapacity:self.length];
for (NSUInteger i = self.length - 1; i < self.length; i--) {
[reversed appendFormat:@"%C", [self characterAtIndex:i]];
}
return [NSString stringWithString:reversed];
}
@end
使用类别:
#import <Foundation/Foundation.h>
#import "NSString+Additions.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString *original = @"Hello";
NSString *reversed = [original reversedString];
NSLog(@"反转后的字符串: %@", reversed);
}
return 0;
}
在上述代码中,为 NSString
类添加了一个 reversedString
方法,用于返回字符串的反转版本。
6.2 协议(Protocol)
协议定义了一组方法声明,但不提供实现,任何类都可以遵守协议并实现协议中的方法。协议常用于实现代理模式等设计模式。
// DelegateProtocol.h
#import <Foundation/Foundation.h>
@protocol DelegateProtocol <NSObject>
@required
- (void)dataReceived:(NSString *)data;
@optional
- (void)connectionClosed;
@end
// NetworkManager.h
#import <Foundation/Foundation.h>
@protocol DelegateProtocol;
@interface NetworkManager : NSObject
@property (nonatomic, weak) id<DelegateProtocol> delegate;
- (void)fetchData;
@end
// NetworkManager.m
#import "NetworkManager.h"
@implementation NetworkManager
- (void)fetchData {
// 模拟数据获取
NSString *data = @"Some data";
if ([self.delegate respondsToSelector:@selector(dataReceived:)]) {
[self.delegate dataReceived:data];
}
}
@end
// ViewController.h
#import <UIKit/UIKit.h>
#import "DelegateProtocol.h"
@interface ViewController : UIViewController <DelegateProtocol>
@end
// ViewController.m
#import "ViewController.h"
#import "NetworkManager.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NetworkManager *manager = [[NetworkManager alloc] init];
manager.delegate = self;
[manager fetchData];
}
- (void)dataReceived:(NSString *)data {
NSLog(@"接收到的数据: %@", data);
}
- (void)connectionClosed {
NSLog(@"连接已关闭");
}
@end
在上述代码中,定义了一个 DelegateProtocol
协议,NetworkManager
类持有一个遵守该协议的代理对象,并在数据获取后调用代理的 dataReceived:
方法。ViewController
类遵守该协议并实现了相关方法,作为 NetworkManager
的代理来处理数据。