创建对象
1 2 3 4 5 6 7 8 9 |
// 会自动释放 NSString* myString = [NSString string]; // 需要手动释放 NSString* myString1 = [[NSString alloc] init]; [myString1 release]; // 初始的值 NSNumber* value = [[NSNumber alloc] initWithFloat:1.0]; |
类
myClass.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// // Header.h // oc_lif // // Created by enlink on 2023/3/16. // #ifndef Header_h #define Header_h #import <Foundation/Foundation.h> @interface myClass : NSObject { @public int a; int b; } -(void)test; -(int)add; @end #endif /* Header_h */ |
myClass.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// // myClass.m // oc_lif // // Created by enlink on 2023/3/16. // #import "myClass.h" @implementation myClass -(void)test { NSLog(@"ts ts ts "); } -(int)add { return a+b; } @end |
main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// // main.m // oc_lif // // Created by enlink on 2023/3/16. // #import "myClass.h" int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... NSLog(@"Hello, World!"); myClass* mc = [myClass new]; // myClass* mc = [[myClass alloc]init]; mc->a = 1; mc->b = 2; [mc add]; [mc test]; } return 0; } |
objc_object
1 2 3 4 5 6 7 |
/// An opaque type that represents an Objective-C class. typedef struct objc_class *Class; /// Represents an instance of a class. struct objc_object { Class _Nonnull isa OBJC_ISA_AVAILABILITY; }; |
消息
-
在
OC
中“函数调用的过程”就是“消息机制”, 简单说,OC
中任何方法的调用,本质都是发送消息Objective-C
的方法调用是通过消息传递机制实现的- 但在底层,消息传递最终也会转化为函数调用,并在栈上执行,只是中间多了一层动态查找过程
-
C++
是直接函数调用(通过固定地址跳转) -
具体消息见下文一些问题
1 2 |
[obj msg]; [obj cricle:3.14]; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@iterface value : NSObject { int val; int min, max, avr; }; -(id)initVal:(int)a min:(int)b max:(int)c; -(int)min; -(int)max; @end @implementation value -(id)initVal:(int)a min:(int)b max:(int)c { self = [supper init]; if (self != nil) { val = avr = a; min = b; max = c; } return self; } @end |
1 2 |
// 多个参数 BOOL result = [myData writeToFile:@"/tmp/log.txt" atomically:NO]; |
id
- 此种类型变量可以存放任何数据类型的变量
继承
oc
不允许多继承关系
animal
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//animal.h #import <Foundation/Foundation.h> @interface animal : NSObject -(void)show; @end //animal.m #import <Foundation/Foundation.h> #import "animal.h" @implementation animal -(void)show{ NSLog(@"the animal show."); } @end |
cat
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//cat.h #import <Foundation/Foundation.h> #import "animal.h" @interface cat : animal -(void)show; @end //cat.m #import <Foundation/Foundation.h> #import "cat.h" @implementation cat -(void)show{ [super show]; NSLog(@"the cat show"); } @end |
dog
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#import <Foundation/Foundation.h> #import "animal.h" @interface dog : animal -(void)show; @end //cat.m #import <Foundation/Foundation.h> #import "dog.h" @implementation dog -(void)show{ [super show]; NSLog(@"the dog show"); } |
main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//main.m #import "animal.h" #import "cat.h" #import "dog.h" int main(int argc, const char * argv[]) { @autoreleasepool { dog *d=[dog new]; [d show]; cat *c=[[cat alloc]init]; [c show]; animal *an=[dog new]; animal *ac=[cat new]; [an show]; [ac show]; } return 0; } |
alloc
- 为对象分配内存空间
- 类方法(由
NSObject
实现) - 返回 未初始化 的对象
- 设置
retainCount = 1
(ARC
环境) - 纯
C
函数性能优化(不通过常规消息派发)
- 类方法(由
- 建立对象的基本元数据(特别是
isa
指针) - 分配后内存布局:
1 2 3 4 5 |
+---------------+-----------------+ | isa 指针 (8字节) | 未初始化内存区域 | +---------------+-----------------+ ^ ^ 对象起始地址 实例变量区域 |
init
- 子类可以重写
init
方法,但需要调用父类init
- 每个
init
方法必须先调用[super init]
- 从
NSObject
开始逐级初始化
- 每个
1 2 3 4 5 6 7 8 |
-(id)init { self=[super init]; if(self!=nil){ //子类专有初始化操作 } return self; } |
dealloc
- 当引用计数归零时自动调用
- 绝对不要手动调用
[obj dealloc]
1 2 3 4 5 6 7 |
- (void) dealloc { // do something [caption release]; [photographer release]; [super dealloc]; } |
引用计数
Objective-C Automatic Reference Counting
修改为NO
retain
增加引用计数release
减少引用计数- 给对象发生
release
消息仅为放弃该对象的所有权,当引用计数为0
时,真正销毁对象的是dealloc
函数。所以如果需要在销毁前做一些其他事情,要重写dealloc
函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... NSLog(@"Hello, World!"); myClass* mc = [myClass new]; // myClass* mc = [[myClass alloc]init]; mc->a = 1; mc->b = 2; [mc add]; [mc test]; printf("myClass count %i.\n", [mc retainCount]); [mc retain]; printf("myClass count %i.\n", [mc retainCount]); [mc release]; printf("myClass count %i.\n", [mc retainCount]); } return 0; } |
1 2 3 4 5 6 7 8 |
-(void)dealloc{//重写dealloc方法而不是release方法 /* release放弃派生类中所有实例变量的所有权 其他释放前的善后工作 */ [super dealloc]; } |
自动释放池
1 2 3 4 5 6 |
// string1 will be released automatically NSString* string1 = [NSString string]; // must release this when done NSString* string2 = [[NSString alloc] init]; [string2 release]; |
1 2 3 4 |
@autoreleasepool{ //操作 //可使用break,return,goto等语句 } |
常量对象
- 常量对象没有引用计数,所以无法释放
- 常量对象的释放需要重写
retain
和release
【需要注意ARC
】
ARC
ARC
(Automatic Reference Counting
,自动引用计数)是一个编译期技术,利用此计数可以简化Objective-C
在内存管理方面的工作量
使用权操作
1 2 3 4 5 6 7 8 |
// 取得使用权 [obj retain]; // 缓存对象 id old_obj = obj; // 赋值,other_obj拿到了obj所有权 other_obj = obj; // 释放 [old_obj release]; |
所有权策略
- 创建对象时(
alloc
,init
)/复制对象时(new copy mutableCopy
)获得对象所有权 - 如果不使用
alloc/init/new/copy/mutableCopy
这些方法,或者不使用retain
来保留一个对象,就不能成为对象的所有者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
//a>MyBuffer的借口 @interface MyBuffer:NSObject{ NSString *filename; char *buffer; FILE *fp; ... } ... @end //b>手动内存管理模式下的dealloc实现 -(void) dealloc{ [filename release]; if(buffer!=NULL){ free(buffer); } if(fp!=NULL){ fclose(fp); } [super dealloc]; } //c>ARC内存管理模式下的dealloc实现 -(void) dealloc{ if(buffer!=NULL){ free(buffer); } if(fp!=NULL){ fclose(fp); } } |
方法簇
- 一个方法要属于某个方法族,除了需要满足返回值和方法类别方面的要求之外,还需满足以下命名规则:
- 选择器同方法族名相同(开头的_可忽略)
- 或选择器的名字由方法族名加上非小写字母开头的字符串构成。
循环引用
1 2 |
[A setfriend:B]; [B setfriend:A]; |
弱引用
1 2 3 |
__week id temp; __week People *w=[[People alloc]initWithName:"Lucy"]; |
块对象
全局块
- 内存位置:编译后的数据区
- 生命周期:与程序相同
- 引用计数:不需要管理
1 2 3 4 |
// 特点:不捕获外部变量 void (^logMessage)(void) = ^{ NSLog(@"Hello, World!"); }; |
栈块
1 2 3 4 5 |
// 特点:捕获外部变量 int count = 10; void (^stackBlock)(void) = ^{ NSLog(@"Count: %d", count); // 捕获变量count }; |
- 内存位置:函数栈空间
- 生命周期:函数结束时自动销毁
- 致命风险:函数返回后使用导致崩溃
堆块
1 2 3 4 |
int value = 20; void (^heapBlock)(void) = [^{ NSLog(@"Value: %d", value); } copy]; |
- 创建方式:显式调用
copy
或传递到需要长期持有的地方 - 内存位置:堆空间
- 生命周期:通过引用计数管理
- 必要操作:不再使用时必须
release
(MRC
) 或自动管理(ARC
)
块的变量捕获机制
变量类型 | 捕获行为 | 内存影响 | 示例 |
局部变量 | 值拷贝 (原始类型) | 创建独立副本 | int a = 5; ^{ a; } |
__block 变量 |
引用捕获 | 共享同一内存 | __block int b = 10; ^{ b++; } |
对象变量 | 强引用捕获 (默认) | 可能引起循环引用 | NSObject *obj; ^{ [obj method]; } |
弱引用对象 | __weak 修饰弱引用捕获 |
避免循环引用 | __weak typeof(obj) weakObj = obj; ^{ [weakObj method]; } |
Log
1 |
NSLog ( @"The current date and time is: %@", [NSDate date] ); |
一些问题
消息机制
- 概念
SEL
(选择器):方法名的唯一标识,本质是char*字符串(类似"viewDidLoad")IMP
(函数指针):指向实际函数实现的指针,类型为id (*IMP)(id, SEL, ...)
- 消息发送表达式会被编译器转化为:
1 2 3 |
[receiver message] objc_msgSend(receiver, @selector(message)); |
- 消息传递流程
- 检查receiver是否为nil,若为nil则直接返回(无崩溃)
- 通过receiver->isa指针找到类对象
- 在类缓存中查找方法实现(快速查找)
每个类有缓存(cache_t),使用SEL作为key查找缓存的IMP - 若缓存未命中,则在类的方法列表中查找(慢速查找)
遍历类的方法列表(包括父类),找到后存入缓存 - 若仍未找到,启动消息转发机制
依次调用resolveInstanceMethod:
、forwardingTargetForSelector:
、methodSignatureForSelector:
、forwardInvocation
id
为什么能容纳任意对象
- 底层定义
id
是指向objc_object
结构体的通用指针- 任何 Objective-C 对象在内存中第一块数据必然是
isa
指针(占 8 字节)
1 |
typedef struct objc_object *id; // 核心定义 |
1 2 3 |
struct objc_object { Class isa; // 所有OC对象共享的结构头部 }; |
- 核心机制:通用指针
id
只关心对象头部的isa
指针- 不关心对象具体类型:编译器不做强类型检查
- 总结
id
在isa
位置,记录了所保存的数据的类型,但是不关心isa
后面的数据内容- 所以它可以存任何类型的数据,同时也可以根据
isa
的信息把id
对象转换成所保证对象的真正类型
关于继承
- 类结构
1 2 3 4 5 6 |
struct objc_class { Class isa; // 指向元类 (meta class) Class super_class; // 核心继承链路指针 cache_t cache; // 方法缓存 class_data_bits_t bits; // 方法列表/属性/协议 }; |
- 消息查找流程
- 子类缓存
- 子类方法列表
- 继承链爬升
父类缓存
父类方法列表 - 消息转发三阶段
当整条继承链都处理不了消息时
1 2 3 4 5 6 |
Class currentClass = object_getClass(receiver); while (currentClass) { IMP imp = _findImpInClass(currentClass, selector); if (imp) return imp; currentClass = class_getSuperclass(currentClass); // 关键:跳到父类 } |
关于super
- 编译时转换
- 编译器指令
- 运行时定向查找
1 2 3 4 5 6 |
// 编译器将 [super init] 转换为: struct objc_super superInfo = { self, // 接收者仍是子类对象 class_getSuperclass([self class]) // 获取父类 }; objc_msgSendSuper(&superInfo, @selector(init)); |
objc_super
1 2 3 4 |
struct objc_super { __unsafe_unretained id receiver; // 消息接收者(子类实例) __unsafe_unretained Class super_class; // 查找起点的父类 }; |
- 对比
self
super
直接在父类查找,跳过当前类(子类)的查找过程- 实际调用的父类方法在编译时不确定
实际调用取决于运行时self
的父类
特性 | [super method] |
[self method] |
消息接收者 | 子类对象 | 子类对象 |
查找起点 | 直接从父类开始 | 从当前类(子类)开始 |
查找范围 | 父类及其继承链 | 子类→父类→NSObject |
调用方式 | objc_msgSendSuper() |
objc_msgSend() |
典型用途 | 重写方法时调用父类实现 | 常规方法调用 |
声明:本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ 包管理器:各平台安装卸载相关记述09/17
- ♥ Windows 核心编程 _ 内核对象:线程同步二07/30
- ♥ Chromium:多线程通信机制09/03
- ♥ 【华东师大版七年级上册】02/26
- ♥ 排序_快速排序05/08
- ♥ 创建型:原型模式09/25