文档定位:零基础自学、日常开发备查;每个语法模块:定义说明 → OC完整示例 → 等价JS/TS代码 → 语法详解 → 异同对比 → 常见坑
前置约定
-
OC 依赖 Foundation 框架,所有示例默认
#import <Foundation/Foundation.h> -
OC:编译型、静态强类型;JS:解释型、动态弱类型;TS:JS超集,静态类型
-
OC 集合严格区分 不可变 / 可变;JS Array / Object 默认可变
-
OC 字符串前缀必须带
@;JS/TS 字符串直接引号包裹
目录
-
基础入门:日志打印
-
变量、常量、基础数据类型
-
运算符
-
流程控制:if / switch / for / while
-
字符串 NSString / NSMutableString 👉 对应 JS String
-
数组 NSArray / NSMutableArray 👉 对应 JS Array
-
字典 NSDictionary / NSMutableDictionary 👉 对应 JS Object
-
自定义类 & 对象(模型)👉 对应 JS Class
-
方法定义、调用(实例方法
-/ 类方法+)👉 对应 JS 函数 -
循环遍历全套写法对比
-
Block vs JS 回调函数 / 箭头函数(OC高频难点)
-
GCD(OC) vs JS Promise / async await 异步
-
OC Protocol 协议 vs TS Interface / Type
-
内存、引用简单说明(小白够用版)
1. 基础入门:日志打印
定义
程序运行时输出文字、变量到控制台,用来查看数据、排查bug
OC
Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
// 1. 纯文本打印
NSLog(@"Hello Objective-C");
// 2. 占位符拼接变量
NSString *name = @"小明";
NSInteger age = 18;
NSLog(@"姓名:%@,年龄:%ld", name, age);
return 0;
}
OC常用占位符
%@→ 对象(字符串、数组、字典、自定义对象,万能)
%d→ int 整数
%ld→ NSInteger
%f→ float / double 小数
JS
JavaScript
// Node / 浏览器通用
const name = "小明";
const age = 18;
console.log("Hello JavaScript");
console.log(`姓名:${name},年龄:${age}`);
对比说明
-
NSLog自带时间戳、自动换行 ;console.log默认只输出内容 -
OC 使用占位符拼接;JS 推荐模板字符串
${变量}
避坑
OC 文字必须写 @"",写成 "" 会编译报错
2. 变量、常量、基础数据类型
定义
变量:存储空间,可以修改里面的值;常量:定义后不能修改
OC:声明必须写类型,类型一旦确定不能随意更换
JS:不用写类型,同一个变量可以存数字、字符串、布尔
OC
Objective-C
#import <Foundation/Foundation.h>
int main() {
// 常量 const:基础类型常量
const int num = 100;
// 1. 整数
NSInteger age = 18;
int height = 175;
// 2. 小数
float f = 3.14f;
double d = 3.1415926;
// 3. 布尔 YES(真) / NO(假) 大写
BOOL isBoy = YES;
// 4. 字符
char c = 'A';
// 5. 万能对象指针 id(可以接收任意对象,慎用,失去类型校验)
id any = @"随便存";
NSLog(@"%@", any);
return 0;
}
JS
JavaScript
// const 常量,let 变量,var 旧写法不推荐
const num = 100;
let age = 18;
let f = 3.14;
let d = 3.1415926;
let isBoy = true;
let c = 'A';
let any = "随便存";
console.log(any);
对比说明
-
OC 区分
int/NSInteger:NSInteger会自动适配32/64位系统,开发优先用 -
OC 布尔
YES/NO(大写);JStrue/false(小写) -
OC 变量赋值不同类型 → 编译报错;JS 允许随意更换变量类型
避坑
基础类型不能直接放进OC数组/字典,需要包装成 NSNumber 对象
3. 运算符
算术、比较、逻辑运算符,语法大体一致,只列出差异点
OC
Objective-C
#import <Foundation/Foundation.h>
int main() {
int a = 10, b = 3;
NSLog(@"加法 %d", a + b);
NSLog(@"取模 %d", a % b);
BOOL res = (a > 5 && b < 5); // && 并且,|| 或者,! 取反
NSLog(@"%d", res);
return 0;
}
JS
JavaScript
let a = 10, b = 3;
console.log("加法", a + b);
console.log("取模", a % b);
let res = (a > 5 && b < 5);
console.log(res);
差异重点
-
==:OC 比较基础值;OC 对象比较不能用==,要用isEqual: -
JS:
==会自动隐式转换;===严格相等(推荐)
避坑
OC:@"abc" == @"abc" 对比的是内存地址,不是文本内容!
正确写法:[@"abc" isEqual:@"abc"]
4. 流程控制 if / switch / for / while
4.1 if else 判断
OC
Objective-C
NSInteger score = 85;
if (score >= 90) {
NSLog(@"优秀");
} else if (score >= 60) {
NSLog(@"及格");
} else {
NSLog(@"不及格");
}
JS
JavaScript
let score = 85;
if (score >= 90) {
console.log("优秀");
} else if (score >= 60) {
console.log("及格");
} else {
console.log("不及格");
}
4.2 switch
OC
Objective-C
NSInteger level = 2;
switch (level) {
case 1:
NSLog(@"一级");
break;
case 2:
NSLog(@"二级");
break;
default:
NSLog(@"其他");
break;
}
OC 旧版本 switch 不能直接判断字符串;新版本可支持
JS
JavaScript
let level = 2;
switch (level) {
case 1:
console.log("一级");
break;
case 2:
console.log("二级");
break;
default:
console.log("其他");
break;
}
4.3 for 循环
OC
Objective-C
for (int i = 0; i < 5; i++) {
NSLog(@"i = %d", i);
}
JS
JavaScript
for (let i = 0; i < 5; i++) {
console.log("i =", i);
}
4.4 while / do while
OC
Objective-C
int i = 0;
while (i < 3) {
NSLog(@"while i=%d", i);
i++;
}
JS
JavaScript
let i = 0;
while (i < 3) {
console.log("while i=", i);
i++;
}
对比说明
语法几乎一样;区别在条件判断:
OC 条件必须是明确 BOOL YES/NO ,不会自动隐式转布尔
JS 自动隐式转换:0、""、null、undefined 自动视为 false
5. 字符串 NSString / NSMutableString 👉 JS String
定义
-
NSString:不可变字符串,创建后文字不能修改,只能读取、拼接生成新字符串 -
NSMutableString:可变字符串,可以原地追加、删除、替换文本 -
JS
String:字符串原始值,JS 字符串本身不可变,修改都会生成新字符串
OC
Objective-C
#import <Foundation/Foundation.h>
int main() {
// 不可变 NSString
NSString *str1 = @"Hello";
// 拼接(返回新字符串,原字符串不变)
NSString *str2 = [str1 stringByAppendingString:@" World"];
NSLog(@"%@", str2);
// 可变 NSMutableString
NSMutableString *mStr = [NSMutableString stringWithString:@"Hello"];
[mStr appendString:@" World"]; // 追加
[mStr replaceOccurrencesOfString:@"World" withString:@"OC" options:0 range:NSMakeRange(0, mStr.length)];
NSLog(@"%@", mStr);
return 0;
}
JS
JavaScript
let str1 = "Hello";
let str2 = str1 + " World";
console.log(str2);
// JS 没有可变字符串,如需频繁拼接推荐数组join
let arr = ["Hello"];
arr.push(" World");
let mStr = arr.join("");
console.log(mStr);
常用方法对照
| 功能 | OC | JS |
|---|---|---|
| 获取长度 | str.length | str.length |
| 字符串拼接 | stringByAppendingString | + / 模板字符串 |
| 是否包含 | str containsString:@"xx" | str.includes("xx") |
| 截取 | substringWithRange | slice / substring |
避坑
NSString 不能用 appendString,调用会崩溃;频繁文本修改请使用 NSMutableString
6. 数组 NSArray / NSMutableArray 👉 JS Array
定义
-
NSArray:不可变数组,初始化完成后不能增删改元素,只能读取 -
NSMutableArray:可变数组,可以新增、删除、替换元素
⚠️ OC 数组只能存放对象 ,基础数字需要包装成
@10(NSNumber)JS 数组:不限类型,数字、字符串、对象、数组都可以存放,默认可变
OC
Objective-C
#import <Foundation/Foundation.h>
int main() {
// 1. 不可变数组 NSArray(字面量写法推荐)
NSArray *arr = @[@"苹果", @"香蕉", @100];
NSLog(@"取值:%@", arr[0]);
NSLog(@"数组长度:%ld", arr.count);
// 2. 可变数组 NSMutableArray
NSMutableArray *mArr = [NSMutableArray array];
[mArr addObject:@"西瓜"]; // 末尾新增
[mArr insertObject:@"橙子" atIndex:0]; // 指定下标插入
mArr[1] = @"葡萄"; // 修改下标元素
[mArr removeObjectAtIndex:0]; // 删除下标
[mArr removeAllObjects]; // 清空数组
return 0;
}
JS
JavaScript
// JS 数组默认可变,无原生不可变数组
const arr = ["苹果", "香蕉", 100];
console.log("取值:", arr[0]);
console.log("数组长度:", arr.length);
const mArr = [];
mArr.push("西瓜");
mArr.splice(0, 0, "橙子");
mArr[1] = "葡萄";
mArr.splice(0, 1);
mArr.length = 0; // 清空
数组遍历对照
Objective-C
// OC 快速遍历
for (id item in arr) {
NSLog(@"%@", item);
}
JavaScript
for (const item of arr) {
console.log(item);
}
避坑
-
NSArray 调用 addObject → 运行崩溃,必须 NSMutableArray
-
下标越界直接闪退,JS 越界返回 undefined
7. 字典 NSDictionary / NSMutableDictionary 👉 JS Object
定义
-
NSDictionary:不可变键值对,创建完成不能增删改 -
NSMutableDictionary:可变键值对,支持新增、删除、修改key-value
⚠️ OC key、value 都必须是对象 ,常用 NSString 做key
JS Object:key 自动转为字符串,value 任意类型
OC
Objective-C
#import <Foundation/Foundation.h>
int main() {
// 不可变字典
NSDictionary *dict = @{
@"name": @"小明",
@"age": @18
};
NSString *name = dict[@"name"];
NSLog(@"%@", name);
// 可变字典
NSMutableDictionary *mDict = [NSMutableDictionary dictionary];
mDict[@"gender"] = @"男"; // 新增/覆盖
[mDict removeObjectForKey:@"gender"]; // 删除key
mDict removeAllObjects; // 清空
return 0;
}
JS
JavaScript
const dict = {
name: "小明",
age: 18
};
const name = dict.name;
console.log(name);
const mDict = {};
mDict.gender = "男";
delete mDict.gender;
Object.keys(mDict).forEach(k => delete mDict[k]);
字典遍历
Objective-C
for (NSString *key in dict) {
id val = dict[key];
NSLog(@"key:%@ val:%@", key, val);
}
JavaScript
for (const key in dict) {
const val = dict[key];
console.log(`key:${key} val:${val}`);
}
避坑
OC 取值不存在key 返回 nil,JS 返回 undefined
8. 自定义类 & 对象(模型)👉 JS Class
定义
OC:采用 .h(声明) + .m(实现) 文件分离;@property 定义属性
JS:ES6 class 语法糖,单文件,无声明实现分离
OC 完整示例
Person.h
Objective-C
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
// 自定义构造方法
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;
- (void)sayHello;
@end
Person.m
Objective-C
#import "Person.h"
@implementation Person
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
self = [super init];
if (self) {
self.name = name;
self.age = age;
}
return self;
}
- (void)sayHello {
NSLog(@"我叫%@,%ld岁", self.name, self.age);
}
@end
main 使用
Objective-C
Person *p = [[Person alloc] initWithName:@"小红" age:20];
[p sayHello];
JS 等价 Class
JavaScript
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`我叫${this.name},${this.age}岁`);
}
}
const p = new Person("小红", 20);
p.sayHello();
对比说明
-
OC
.h控制对外暴露接口,天然封装;JS 无原生私有属性 -
OC 创建对象:
alloc分配内存 +init初始化 -
OC 属性修饰符
nonatomic/copy/assign/strong用来管理内存,JS 无需关心
9. 方法定义、调用(实例方法 - / 类方法 +)👉 JS 函数
定义
-
-开头:实例方法,必须创建对象后调用 -
+开头:类方法,直接用类名调用,无需实例化
OC 没有独立全局函数,方法全部依附类;JS 支持独立函数
OC
Objective-C
// .h
@interface Demo : NSObject
+ (void)classFunc; // 类方法
- (void)instanceFunc:(NSString *)msg; // 实例方法,带参数
@end
// .m
@implementation Demo
+ (void)classFunc {
NSLog(@"这是类方法");
}
- (void)instanceFunc:(NSString *)msg {
NSLog(@"实例方法,参数:%@", msg);
}
@end
// 调用
[Demo classFunc];
Demo *d = [[Demo alloc] init];
[d instanceFunc:@"测试消息"];
JS
JavaScript
// 独立函数(OC没有等价全局方法)
function globalFunc() {
console.log("全局函数");
}
globalFunc();
class Demo {
static classFunc() { // static 静态方法 ≈ OC +类方法
console.log("这是静态方法");
}
instanceFunc(msg) {
console.log("实例方法,参数:", msg);
}
}
Demo.classFunc();
const d = new Demo();
d.instanceFunc("测试消息");
对比说明
OC 调用语法:[对象 方法:参数] 消息发送机制
JS:对象.方法()
10. 循环遍历全套写法对比(备查)
汇总多种遍历方式,开发查阅使用
Objective-C
NSArray *arr = @[@"A", @"B", @"C"];
// 1. 传统for循环
for (int i = 0; i < arr.count; i++) {
NSLog(@"下标%d:%@", i, arr[i]);
}
// 2. forin 快速遍历
for (id obj in arr) {
NSLog(@"元素:%@", obj);
}
// 3. Block回调遍历(重点,和下一章联动)
[arr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(@"下标%lu 值%@", idx, obj);
}];
JavaScript
const arr = ["A","B","C"];
// 1. 传统for
for(let i = 0; i < arr.length; i++){
console.log(`下标${i}:`, arr[i]);
}
// 2. for of
for(const item of arr){
console.log("元素:", item);
}
// 3. forEach 回调遍历
arr.forEach((item, idx)=>{
console.log(`下标${idx} 值${item}`);
})
11. Block vs JS 回调函数 / 箭头函数(OC高频难点 · 备查重点)
定义
-
Block(OC):代码块,一段可保存、传递、延迟执行的代码;多用于回调、异步、遍历。等价 JS 回调/箭头函数
-
JS 回调函数:函数作为参数传递,等待时机执行;箭头函数为简写形式
核心共同点:函数可传参、延迟执行、捕获外部变量
核心差异:Block 涉及内存管理(
__block、循环引用);JS 由GC自动回收
11.1 Block 基础语法 + JS 对照
OC Block 基础格式
Objective-C
// 返回值(^block名称)(参数) = ^(参数){代码};
void (^myBlock)(NSString *) = ^(NSString *msg) {
NSLog(@"Block收到消息:%@", msg);
};
// 调用
myBlock(@"Hello Block");
JS 普通回调函数
JavaScript
const myFunc = function(msg) {
console.log("回调收到消息:", msg);
}
myFunc("Hello Callback");
JS 箭头函数简写(工程常用)
JavaScript
const myFunc = (msg) => {
console.log("回调收到消息:", msg);
}
myFunc("Hello Callback");
11.2 Block 作为方法参数(网络回调经典场景)
OC
Objective-C
#import <Foundation/Foundation.h>
- (void)requestDataWithCallback:(void (^)(NSString *result))callback {
// 模拟异步耗时操作
NSString *res = @"接口返回数据";
callback(res);
}
// 使用
[self requestDataWithCallback:^(NSString *result) {
NSLog(@"回调结果:%@", result);
}];
JS 等价写法
JavaScript
function requestDataWithCallback(callback) {
const res = "接口返回数据";
callback(res);
}
requestDataWithCallback((result) => {
console.log("回调结果:", result);
});
11.3 Block 捕获外部变量(重点难点)
OC 默认:值拷贝,无法直接修改外部基础变量
Objective-C
int num = 10;
void (^block)(void) = ^{
// num = 99; // 编译报错,默认只读
NSLog(@"block读取num:%d", num);
};
num = 99;
block(); // 输出 10,捕获时拷贝快照
OC 使用 __block 允许修改外部变量
Objective-C
__block int num = 10;
void (^block)(void) = ^{
num = 99;
NSLog(@"block读取num:%d", num);
};
num = 88;
block(); // 输出99
JS 对比:引用捕获,可直接修改
JavaScript
let num = 10;
const func = () => {
num = 99;
console.log("回调读取num:", num);
};
num = 88;
func(); // 输出99
11.4 Block 循环引用(OC高频坑,JS一般不用手动处理)
ARC下 self 持有block,block内部强引用self → 循环引用,内存泄漏
✅ OC 解决方案:
weakSelf
Objective-C
__weak typeof(self) weakSelf = self;
self.testBlock = ^{
NSLog(@"%@", weakSelf.name);
};
JS:GC自动处理循环引用,绝大多数场景无需手动解除
11.5 场景对照表
| 场景 | OC Block | JS 回调 / 箭头函数 |
|---|---|---|
| 数组遍历回调 | enumerateObjectsUsingBlock | forEach |
| 网络请求完成回调 | block参数 | callback / then |
| UI动画完成回调 | completion block | 回调函数 |
| 排序比较器 | sortedArrayUsingComparator | array.sort |
| 延迟执行 | dispatch_after + block | setTimeout |
11.6 避坑清单
-
Block 默认不能修改外部基础变量,修改需要
__block -
成员变量保存的block,内部使用self必须weak,防止循环引用
-
Block语法
^容易遗漏,强类型校验严格 -
JS箭头函数不绑定this;Block捕获self默认强引用
11.7 typedef 简化冗长Block声明(项目标准写法)
Objective-C
// 定义别名
typedef void(^ResultCallback)(NSString *result);
- (void)getData:(ResultCallback)callback {
callback(@"数据");
}
12. GCD(OC) vs JS Promise / async await 异步编程(备查重点)
定义
-
GCD(Grand Central Dispatch):苹果官方C实现多线程调度API,管理队列、线程、异步任务,搭配Block。
-
Promise:JS异步容器,存放异步结果,解决回调地狱;
-
async / await:Promise语法糖,扁平化异步代码,可读性更高。
核心映射关系
-
GCD队列派发异步任务 ≈ JS Promise
-
GCD串行队列 ≈ Promise链式
.then() -
GCD dispatch_group 等待全部并发完成 ≈
Promise.all() -
async/await≈ GCD信号量同步等待(写法扁平化) -
主队列
dispatch_get_main_queue()≈ JS主线程回调
重要底层差异:
GCD 支持多核真正并行多线程 ;JS 为单线程事件循环,异步仅任务排队,无法多核并行计算。
12.1 基础异步执行(GCD async vs Promise)
OC GCD
Objective-C
#import <Foundation/Foundation.h>
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(globalQueue, ^{
NSLog(@"子线程:执行耗时操作(网络/文件)");
sleep(1);
// 切回主线程刷新UI
dispatch_async(mainQueue, ^{
NSLog(@"主线程:刷新UI");
});
});
JS Promise
JavaScript
function fetchData() {
return new Promise((resolve) => {
console.log("异步:执行耗时操作(网络/文件)");
setTimeout(() => {
resolve("请求结果");
}, 1000);
});
}
fetchData().then(res => {
console.log("主线程:拿到结果,更新页面", res);
});
12.2 串行依次执行任务(串行队列 vs Promise then / async await)
场景:A完成后执行B,B完成后执行C
OC GCD 串行队列
Objective-C
dispatch_queue_t serialQueue = dispatch_queue_create("com.demo.serial", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
NSLog(@"任务1");
sleep(1);
});
dispatch_async(serialQueue, ^{
NSLog(@"任务2");
sleep(1);
});
dispatch_async(serialQueue, ^{
NSLog(@"任务3");
});
JS Promise 链式调用
JavaScript
function task(val, delay) {
return new Promise(resolve => {
setTimeout(() => {
console.log(val);
resolve();
}, delay);
})
}
task("任务1",1000)
.then(()=>task("任务2",1000))
.then(()=>task("任务3",0));
JS async await(推荐扁平化写法)
JavaScript
async function runSerial() {
await task("任务1",1000);
await task("任务2",1000);
await task("任务3",0);
}
runSerial();
12.3 并发执行多个任务,等待全部完成(dispatch_group vs Promise.all)
OC dispatch_group
Objective-C
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, globalQueue, ^{
sleep(1);
NSLog(@"并发任务A");
});
dispatch_group_async(group, globalQueue, ^{
sleep(2);
NSLog(@"并发任务B");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"所有并发任务执行完毕");
});
JS Promise.all
JavaScript
const p1 = task("并发任务A", 1000);
const p2 = task("并发任务B", 2000);
Promise.all([p1, p2]).then(() => {
console.log("所有并发任务执行完毕");
});
12.4 延迟执行(dispatch_after vs setTimeout)
OC
Objective-C
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"延迟2秒执行");
});
JS
JavaScript
setTimeout(()=>{
console.log("延迟2秒执行");
},2000);
12.5 仅执行一次(dispatch_once vs JS闭包单例)
OC
Objective-C
static id instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
JS
JavaScript
let instance = null;
function getInstance(){
if(!instance){
instance = {};
}
return instance;
}
12.6 API对照表
| 功能 | OC GCD | JS |
|---|---|---|
| 异步子线程执行 | dispatch_async(globalQueue, block) | new Promise / setTimeout |
| 切回主线程 | dispatch_async(mainQueue, block) | .then / await 默认主线程 |
| 串行顺序执行 | DISPATCH_QUEUE_SERIAL | Promise then链 / async await |
| 并发等待全部完成 | dispatch_group + notify | Promise.all |
| 任意一个任务完成即返回 | 手动分组标记 | Promise.race |
| 延迟调度 | dispatch_after | setTimeout |
| 代码只运行一次 | dispatch_once | 闭包缓存实例 |
| 同步阻塞等待 | dispatch_group_wait / semaphore | await(非阻塞主线程) |
12.7 避坑清单
OC GCD
-
UI操作必须在主队列执行,子线程刷新UI会崩溃异常
-
dispatch_sync同步派发极易引发死锁(串行队列同步派发自身队列) -
semaphore信号量慎用,容易阻塞线程
-
异步回调block依旧存在self循环引用风险,使用weakSelf
JS 异步
-
JS单线程,大量同步计算会阻塞页面,无法多核并行运算
-
async函数永远返回Promise
-
Promise异常未写
.catch()会抛出未捕获错误 -
await 只能写在 async 函数内部
12.8 回调地狱说明
多层嵌套回调可读性差:
-
OC:多层Block嵌套 → 推荐dispatch_group、拆分方法
-
JS:多层回调嵌套 → 推荐 Promise / async await
13. OC Protocol 协议 vs TS Interface / Type 类型别名(备查重点)
定义
-
OC Protocol(协议) :一套方法声明契约,定义可选/必选方法,可声明属性,不提供实现;类遵循协议并实现对应方法。常用于delegate代理、解耦、面向接口编程。
-
TS Interface :描述对象结构、方法契约,约束属性与方法签名;类可使用
implements实现接口。 -
TS Type Alias(type):类型别名,和Interface大部分场景互通,支持联合、交叉类型。
✅ 核心对应
-
@protocol≈interface -
类
<Protocol>遵循协议 ≈ TS classimplements interface -
@required必实现 ≈ interface 非可选成员 -
@optional可选实现 ≈ interface?可选成员 -
id<Protocol>协议类型变量 ≈ TS 变量声明为接口类型 -
协议继承
@protocol P1 <P2>≈interface A extends B
⚠️ 关键差异
-
OC Protocol 运行时保留信息,可
conformsToProtocol:判断;TS接口编译后擦除,仅编译期校验 -
OC协议主要约束方法;TS Interface同时约束属性和方法
-
原生JS没有接口能力,仅能注释人工约定,无编译校验
13.1 最简示例:定义协议 / 接口
OC Protocol
Objective-C
// 定义协议
@protocol UserProtocol <NSObject>
// @required 默认开启,可以省略
- (NSString *)getUserName;
- (void)sayHi;
// @optional 可选实现
@optional
- (void)doSomething;
@end
// 类遵循协议 <>
@interface User : NSObject <UserProtocol>
@end
@implementation User
- (NSString *)getUserName {
return @"小明";
}
- (void)sayHi {
NSLog(@"Hi!");
}
// doSomething 可选,允许不实现
@end
TS Interface 等价
TypeScript
interface UserProtocol {
getUserName(): string;
sayHi(): void;
doSomething?(): void; // ? 等价 @optional
}
class User implements UserProtocol {
getUserName(): string {
return "小明";
}
sayHi(): void {
console.log("Hi!");
}
}
TS Type 别名等价
TypeScript
type UserProtocol = {
getUserName: () => string;
sayHi: () => void;
doSomething?: () => void;
};
原生 JS(注释模拟,无编译校验)
JavaScript
/**
* @typedef {Object} UserProtocol
* @property {()=>string} getUserName
* @property {()=>void} sayHi
* @property {(()=>void)|undefined} [doSomething]
*/
class User {
getUserName() { return "小明"; }
sayHi() { console.log("Hi!"); }
}
13.2 协议作为参数类型(id)
OC
Objective-C
void printUser(id<UserProtocol> user) {
NSLog(@"%@", [user getUserName]);
}
id<UserProtocol> u = [[User alloc] init];
printUser(u);
TS
TypeScript
function printUser(user: UserProtocol) {
console.log(user.getUserName());
}
const u: UserProtocol = new User();
printUser(u);
13.3 协议继承扩展
OC
Objective-C
@protocol BaseProtocol <NSObject>
- (void)baseFunc;
@end
@protocol SubProtocol <BaseProtocol>
- (void)subFunc;
@end
@interface Demo : NSObject <SubProtocol>
@end
TS interface extends
TypeScript
interface BaseProtocol {
baseFunc(): void;
}
interface SubProtocol extends BaseProtocol {
subFunc(): void;
}
class Demo implements SubProtocol {
baseFunc(): void {}
subFunc(): void {}
}
13.4 经典场景:Delegate 代理模式(iOS高频用法)
OC 完整示例
Objective-C
@protocol DataLoaderDelegate <NSObject>
- (void)onLoadSuccess:(NSString *)result;
@optional
- (void)onLoadFail:(NSError *)error;
@end
@interface DataLoader : NSObject
@property (nonatomic, weak) id<DataLoaderDelegate> delegate;
- (void)loadData;
@end
@implementation DataLoader
- (void)loadData {
dispatch_async(dispatch_get_global_queue(0,0), ^{
NSString *res = @"加载完成";
dispatch_async(dispatch_get_main_queue(), ^{
if ([self.delegate respondsToSelector:@selector(onLoadSuccess:)]) {
[self.delegate onLoadSuccess:res];
}
});
});
}
@end
@interface ViewController : NSObject <DataLoaderDelegate>
@end
@implementation ViewController
- (void)onLoadSuccess:(NSString *)result {
NSLog(@"回调收到:%@", result);
}
@end
TS 等价实现
TypeScript
interface DataLoaderDelegate {
onLoadSuccess(result: string): void;
onLoadFail?(error: Error): void;
}
class DataLoader {
delegate?: DataLoaderDelegate;
loadData() {
setTimeout(() => {
const res = "加载完成";
this.delegate?.onLoadSuccess(res);
}, 1000);
}
}
class ViewController implements DataLoaderDelegate {
onLoadSuccess(result: string): void {
console.log("回调收到:", result);
}
}
补充:现代JS/TS 更多使用Promise替代Delegate回调;iOS原生控件大量依赖Protocol Delegate
13.5 能力对比表
| 能力 | OC @protocol | TS interface | TS type |
|---|---|---|---|
| 定义方法契约 | ✅ | ✅ | ✅ |
| 定义属性结构 | ✅ | ✅ | ✅ |
| 继承扩展 | ✅ <> |
✅ extends | ✅ &交叉类型 |
| 同名定义自动合并 | ❌ | ✅ | ❌ |
| 联合/元组/基础类型包装 | ❌ | ❌ | ✅ |
| 编译期类型校验 | ❌(运行时) | ✅ | ✅ |
| 运行时保留类型信息 | ✅ conformsToProtocol | ❌ 编译擦除 | ❌ |
选型建议
-
对标OC协议、类实现契约、模块对外接口 →
interface -
联合类型、工具类型、简单别名 →
type
13.6 避坑清单
OC Protocol
-
delegate 属性必须使用
weak,防止循环引用内存泄漏 -
调用 optional 方法前必须
respondsToSelector:判断,否则崩溃 -
协议声明property,类需要手动合成getter/setter
-
协议建议继承
<NSObject>,才能使用conformsToProtocol:、respondsToSelector:
TS Interface / Type
-
TS接口编译后删除,运行时无法判断对象是否实现接口
-
interface 支持声明合并,容易意外覆盖定义;type不支持合并
-
implements仅做编译约束,不会自动生成方法,必须手动实现全部必选成员
13.7 泛型协议简要对比
OC
Objective-C
@protocol ResponseProtocol <NSObject>
- (void)handleData:(id)data;
@end
OC泛型能力偏弱,一般搭配泛型容器使用
TS 泛型接口
TypeScript
interface ResponseProtocol<T> {
handleData(data: T): void;
}
14. 内存、引用简单说明(小白够用版)
-
OC:ARC 自动引用计数,对象使用
strong强引用;引用计数归零自动释放;区分copy / assign / weak修饰符管理内存。 -
JS/TS:自动垃圾回收 GC,开发者不需要手动管理内存释放。
-
值传递 vs 引用传递:
-
OC:基础类型值传递;对象传递指针(引用)
-
JS/TS:基础原始类型值传递;对象、数组为引用传递
-
-
常见内存问题:OC容易出现循环引用,需要手动weak;JS现代GC可自动处理绝大多数循环引用场景。