杰瑞科技汇

Objective-C基础教程

前言:为什么学习 Objective-C?

尽管现在 Apple 主推 Swift 语言,但 Objective-C 仍然是:

Objective-C基础教程-图1
(图片来源网络,侵删)
  1. 维护旧项目:大量成熟的 Apple 应用和第三方库都是用 Objective-C 编写的。
  2. 理解底层:学习 Objective-C 能让你更深入地理解 Objective-C Runtime,从而更好地理解 Swift 的一些底层机制和消息转发机制。
  3. 生态系统兼容:当你需要使用某个只有 Objective-C 版本的库时,你必须能读懂它。

第一部分:核心概念 - 从 C 到面向对象

Objective-C 是 C 语言的超集,这意味着任何合法的 C 代码都是合法的 Objective-C 代码,它在此基础上增加了 Smalltalk 风格的面向对象特性。

语法特点:消息传递 vs. 函数调用

这是理解 Objective-C 的关键。

  • C/Java/C# 风格(函数调用)[对象 函数] 这种方式更像是直接调用对象的一个方法,强调“行为”。
  • Objective-C 风格(消息传递)[对象 消息: 参数] 这种方式更像是给对象发送一个消息,对象收到消息后决定如何响应(即调用哪个方法),它强调“通信”。

示例:

// C 风格函数调用
int result = add(3, 5); 
// Objective-C 风格消息传递
NSString *greeting = @"Hello";
NSUInteger length = [greeting length]; // 给 greeting 对象发送 "length" 消息

关键字和符号

Objective-C 引入了一些独特的符号,初学者可能会感到困惑:

Objective-C基础教程-图2
(图片来源网络,侵删)
  • 用于标记 Objective-C 特有的类型或字面量。
    • @interface, @implementation, @end:定义类和实现类的开始与结束。
    • @class:向前声明一个类。
    • @property:声明一个属性。
    • @synthesize / @dynamic:合成或动态生成属性的 getter/setter 方法(现代 Xcode 中 @synthesize 通常是自动的)。
    • @selector():获取一个方法的 SEL(方法选择器)。
    • 定义 NSString 字符串字面量。
    • @YES, @NO:定义布尔值。
    • @16:定义 NSNumber 数值字面量。
  • 指针符号,在 Objective-C 中,所有对象(如 NSString, NSArray)都通过指针来引用,所以声明对象变量时,类型名前总有一个 。
  • (减号) 和 (加号):
    • (实例方法):作用于类的实例(对象)上,必须先创建对象才能调用。
    • (类方法):作用于类本身,无需创建实例即可调用,通常用于创建新实例或作为工具方法。

第二部分:面向对象编程

类的声明与实现

一个完整的类分为两部分:接口(声明)和实现

a. 接口

.h 文件中定义,告诉编译器这个类有什么(属性和方法)。

// Person.h
#import <Foundation/Foundation.h> // Apple 基础框架,必须导入
// @interface 声明一个名为 Person 的类,继承自 NSObject
// NSObject 是所有 Objective-C 类的根类
@interface Person : NSObject
// --- 属性 ---
// @property 声明一个名为 name 的属性
// strong: 强引用,表示该属性对对象拥有所有权,ARC (自动引用计数) 会自动管理内存。
// nonatomic: 非原子性,性能更高,在多线程环境下不安全。
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age; // assign: 用于基本数据类型,不涉及内存管理
// --- 方法 ---
// 实例方法,以 - 开头
- (void)sayHello;
// 类方法,以 + 开头,用于创建新实例
+ (instancetype)personWithName:(NSString *)name andAge:(NSInteger)age;
@end

b. 实现

.m 文件中定义,告诉编译器这些属性和方法具体是如何工作的。

// Person.m
#import "Person.h" // 导入自己的头文件
@implementation Person
// @synthesize 自动为 name 属性生成 _name 成员变量和 getter/setter 方法
// (现代 Xcode 默认自动完成,可以不写)
@synthesize name = _name;
// 实现 sayHello 方法
- (void)sayHello {
    // NSLog 是 Foundation 框架中的打印函数,类似于 printf
    // %@ 是一个格式说明符,用于打印对象
    NSLog(@"Hello, my name is %@ and I'm %ld years old.", self.name, (long)self.age);
}
// 实现 personWithName:andAge 类方法
+ (instancetype)personWithName:(NSString *)name andAge:(NSInteger)age {
    // alloc: 分配内存,创建一个未初始化的 Person 对象
    // init: 初始化对象
    // self: 在方法内部指代当前实例(在类方法中指代当前类)
    return [[self alloc] initWithName:name andAge:age];
}
// 私有的初始化方法
- (instancetype)initWithName:(NSString *)name andAge:(NSInteger)age {
    // 调用父类的初始化方法
    self = [super init];
    if (self) {
        // 初始化成员变量
        _name = [name copy]; // 对字符串使用 copy 是一个好习惯,防止外部修改
        _age = age;
    }
    return self;
}
@end

使用类

#import <Foundation/Foundation.h>
#import "Person.h" // 导入我们刚刚定义的 Person 类
int main(int argc, const char * argv[]) {
    @autoreleasepool { // 自动释放池,用于管理内存
        // 1. 创建 Person 对象
        Person *person1 = [Person personWithName:@"Alice" andAge:30];
        // 2. 调用方法
        [person1 sayHello]; // 输出: Hello, my name is Alice and I'm 30 years old.
        // 3. 访问属性
        person1.age = 31;
        NSLog(@"%@ is now %ld years old.", person1.name, (long)person1.age);
    }
    return 0;
}

第三部分:Foundation 框架常用类

Foundation 框架是 Objective-C 的基石,提供了大量核心数据类型和实用工具。

Objective-C基础教程-图3
(图片来源网络,侵删)

NSString

字符串是不可变的(一旦创建,内容不能改变)。

NSString *str1 = @"Hello"; // 字面量创建
NSString *str2 = [[NSString alloc] initWithFormat:@"World, %@", str1]; // 格式化创建
// 常用方法
NSUInteger len = str1.length; // 获取长度
BOOL hasHello = [str1 containsString:@"lo"]; // 是否包含子串
NSString *upperStr = str1.uppercaseString; // 转大写

NSMutableString

可变字符串。

NSMutableString *mutableStr = [NSMutableString stringWithString:@"Hello"];
[mutableStr appendString:@" World!"]; // 追加字符串
[mutableStr replaceCharactersInRange:NSMakeRange(6, 5) withString:@"Objective-C"]; // 替换

NSArray

不可变数组,存放对象。

NSArray *array1 = @[@"One", @"Two", @"Three"]; // 字面量创建,最常用
NSArray *array2 = [NSArray arrayWithObjects:@"A", @"B", @"C", nil]; // 传统方式
// 访问元素
NSString *firstItem = array1.firstObject; // 获取第一个对象,比 objectAtIndex: 更安全
NSString *secondItem = array1[1]; // 使用下标访问 (iOS 6+)
// 获取数量
NSUInteger count = array1.count;

NSMutableArray

可变数组。

NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:array1];
[mutableArray addObject:@"Four"]; // 添加对象
[mutableArray removeObjectAtIndex:0]; // 移除对象

NSDictionary

不可变字典,键值对存储。

NSDictionary *dict1 = @{@"name": @"Bob", @"age": @25}; // 字面量创建
NSDictionary *dict2 = [NSDictionary dictionaryWithObjectsAndKeys:@"Alice", @"name", @30, @"age", nil];
// 访问值
NSString *name = dict1[@"name"]; // 通过键访问

NSMutableDictionary

可变字典。

NSMutableDictionary *mutableDict = [NSMutableDictionary dictionaryWithDictionary:dict1];
[mutableDict setObject:@"Engineer" forKey:@"job"]; // 添加/修改键值对
[mutableDict removeObjectForKey:@"age"]; // 移除键值对

第四部分:内存管理 (Modern Objective-C with ARC)

ARC (Automatic Reference Counting) 是现代 Objective-C 的自动内存管理机制,开发者无需手动调用 retain, release, autorelease

强引用 vs. 弱引用

  • strong (默认): 强引用,只要有一个强引用指向对象,对象就不会被销毁。
  • weak: 弱引用,不增加对象的引用计数,当对象被销毁时,弱引用会自动被置为 nil,主要用于避免循环引用

循环引用示例:

@interface A : NSObject
@property (nonatomic, strong) B *bObj;
@end
@interface B : NSObject
@property (nonatomic, strong) A *aObj;
@end
// 当 aObj 和 bObj 互相强引用时,它们将永远不会被释放,导致内存泄漏。

解决方案:使用 weak

// 在 B.h 中
@property (nonatomic, weak) A *aObj; // 将 aObj 改为 weak 引用

@property 修饰符总结

修饰符 适用类型 作用
strong 对象类型 默认值,拥有对象所有权
weak 对象类型 不拥有所有权,用于打破循环引用
copy 对象类型 (如 NSString, NSArray) 创建一个副本,防止外部修改影响内部值
assign 基本数据类型 (NSInteger, CGRect, BOOL) 简单赋值,不涉及内存管理
readonly 所有 只生成 getter 方法,不生成 setter
readwrite 所有 同时生成 getter 和 setter (默认)

第五部分:块

块是 C 语言、Objective-C 和 Swift 中的匿名函数,类似于其它语言中的闭包或 Lambda 表达式,它捕获了定义时的作用域,可以在任何地方执行。

声明和使用

// 定义一个块变量,它没有参数,返回一个 NSString
NSString (^myBlock)(void) = ^{
    return @"This is a block!";
};
// 调用块
NSLog(@"%@", myBlock());
// 带参数和返回值的块
NSInteger (^addBlock)(NSInteger, NSInteger) = ^(NSInteger a, NSInteger b) {
    return a + b;
};
NSLog(@"%ld", (long)addBlock(10, 20));

在方法中使用块

这在异步操作(如网络请求)中非常常见。

// 定义一个使用块作为参数的方法
- (void)performTaskWithCompletion:(void (^)(NSString *result, NSError *error))completion {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 模拟耗时操作
        [NSThread sleepForTimeInterval:2.0];
        NSString *result = @"Task completed!";
        NSError *error = nil;
        // 在主线程中执行回调
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completion) {
                completion(result, error);
            }
        });
    });
}
// 调用方法
[self performTaskWithCompletion:^(NSString *result, NSError *error) {
    if (error) {
        NSLog(@"Error: %@", error.localizedDescription);
    } else {
        NSLog(@"Success: %@", result); // 2秒后输出: Success: Task completed!
    }
}];

总结与下一步

恭喜你,你已经掌握了 Objective-C 的基础知识!现在你可以:

  1. 阅读和理解 大量的开源 Objective-C 代码和 Apple 的旧版示例。
  2. 维护和修改 现有的 Objective-C 项目。
  3. 为 Swift 项目编写 Objective-C 混编代码

下一步学习建议:

  • 深入 Runtime:了解 isa 指针、消息转发机制 (method swizzling)。
  • Core Data:Apple 的持久化框架。
  • 网络编程:使用 NSURLSession 进行网络请求。
  • 与 Swift 互操作:学习如何在同一个项目中混合使用 Swift 和 Objective-C。

Objective-C 的语法可能看起来有些繁琐,但它是一门非常强大和灵活的语言,深刻理解它将极大地提升你在 Apple 平台开发领域的深度和广度。

分享:
扫描分享到社交APP
上一篇
下一篇