iOS底层学习-day-24
前言-OC-内存管理篇
我是一名iOS开发者, iOS底层 菜鸟的进阶之路30天。
问题
- 下面的运行结果为什么回崩溃
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 1000; i++) {
dispatch_async(queue, ^{
// 加锁
self.name = [NSString stringWithFormat:@"abcdefghijk”];//@"abcdefghijk”太长是一个OC对象
// 解锁
});
}
- 原因
//因为这样,多线程访问多次[_name release];直接导致坏内存访问
- (void)setName:(NSString *)name {
if (_name != name) {
[_name release];
_name = [name retain];
}
}
- 下面的形式为什么不会崩溃
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 1000; i++) {
dispatch_async(queue, ^{
self.name = [NSString stringWithFormat:@"abc”];//@"abc”是一个Tagged Pointer,直接存储到地址中
});
}
Tagged Pointer
-
从64bit开始,iOS引入了Tagged Pointer技术,用于优化NSNumber、NSDate、NSString等小对象的存储
-
在没有使用Tagged Pointer之前, NSNumber等对象需要动态分配内存、维护引用计数等,NSNumber指针存储的是堆中NSNumber对象的地址值
-
使用Tagged Pointer之后,NSNumber指针里面存储的数据变成了:Tag + Data,也就是将数据直接存储在了指针中
-
当指针不够存储数据时,才会使用动态分配内存的方式来存储数据
-
objc_msgSend能识别Tagged Pointer,比如NSNumber的intValue方法,直接从指针提取数据,节省了以前的调用开销
-
如何判断一个指针是否为Tagged Pointer?
- iOS平台,最高有效位是1(第64bit)
- Mac平台,最低有效位是1
GCD定时器
- 例子1: 创建一个GCD定时器
@interface ViewController ()
@property (strong, nonatomic) dispatch_source_t timer;
@end
- (void)test {
// 0.队列
// dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_queue_t queue = dispatch_queue_create("timer", DISPATCH_QUEUE_SERIAL);
// 1.创建定时器
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 2.设置时间
uint64_t start = 2.0; // 2秒后开始执行
uint64_t interval = 1.0; // 每隔1秒执行
dispatch_source_set_timer(timer,
dispatch_time(DISPATCH_TIME_NOW, start * NSEC_PER_SEC),
interval * NSEC_PER_SEC, 0);//interval * NSEC_PER_SEC 1秒乘以纳秒
// 3.设置回调
// dispatch_source_set_event_handler(timer, ^{
// NSLog(@"1111");
// });
dispatch_source_set_event_handler_f(timer, timerFire);
// 4.启动定时器
dispatch_resume(timer);
self.timer = timer;
}
void timerFire(void *param) {
NSLog(@"2222 - %@", [NSThread currentThread]);
}
- 例子2: 对例子1优化
- MJTimer.h
#import <Foundation/Foundation.h>
@interface MJTimer : NSObject
+ (NSString *)execTask:(void(^)(void))task
start:(NSTimeInterval)start
interval:(NSTimeInterval)interval
repeats:(BOOL)repeats
async:(BOOL)async;
+ (NSString *)execTask:(id)target
selector:(SEL)selector
start:(NSTimeInterval)start
interval:(NSTimeInterval)interval
repeats:(BOOL)repeats
async:(BOOL)async;
+ (void)cancelTask:(NSString *)name;
@end
- MJTimer.m
#import "MJTimer.h"
@implementation MJTimer
static NSMutableDictionary *timers_;
dispatch_semaphore_t semaphore_;
+ (void)initialize {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
timers_ = [NSMutableDictionary dictionary];
semaphore_ = dispatch_semaphore_create(1);
});
}
+ (NSString *)execTask:(void (^)(void))task start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async {
if (!task || start < 0 || (interval <= 0 && repeats)) return nil;
// 队列
dispatch_queue_t queue = async ? dispatch_get_global_queue(0, 0) : dispatch_get_main_queue();
// 创建定时器
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 设置时间
dispatch_source_set_timer(timer,
dispatch_time(DISPATCH_TIME_NOW, start * NSEC_PER_SEC),
interval * NSEC_PER_SEC, 0);
dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
// 定时器的唯一标识
NSString *name = [NSString stringWithFormat:@"%zd", timers_.count];
// 存放到字典中
timers_[name] = timer;
dispatch_semaphore_signal(semaphore_);
// 设置回调
dispatch_source_set_event_handler(timer, ^{
task();
if (!repeats) { // 不重复的任务
[self cancelTask:name];
}
});
// 启动定时器
dispatch_resume(timer);
return name;
}
+ (NSString *)execTask:(id)target selector:(SEL)selector start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async {
if (!target || !selector) return nil;
return [self execTask:^{
if ([target respondsToSelector:selector]) {//先判断有没有
#pragma clang diagnostic push//去掉警告
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[target performSelector:selector];
#pragma clang diagnostic pop
}
} start:start interval:interval repeats:repeats async:async];
}
+ (void)cancelTask:(NSString *)name {
if (name.length == 0) return;
dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
dispatch_source_t timer = timers_[name];
if (timer) {
dispatch_source_cancel(timer);
[timers_ removeObjectForKey:name];
}
dispatch_semaphore_signal(semaphore_);
}
@end
- 例子2 使用
@property (copy, nonatomic) NSString *task;
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"begin");
// 接口设计
self.task = [MJTimer execTask:self
selector:@selector(doTask)
start:2.0
interval:1.0
repeats:YES
async:NO];
// self.task = [MJTimer execTask:^{
// NSLog(@"111111 - %@", [NSThread currentThread]);
// } start:2.0 interval:-10 repeats:NO async:NO];
}
- (void)doTask {
NSLog(@"doTask - %@", [NSThread currentThread]);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[MJTimer cancelTask:self.task];
}
内存布局
- 字符串常量 - 已初始化的全局变量、静态变量 - 未初始化的全局变量、静态变量 - 堆 - 栈
- 例子:
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int a = 10;
int b;//数据段:未初始化数据:未初始化的全局变量、静态变量等
int main(int argc, char * argv[]) {
@autoreleasepool {
static int c = 20;//数据段:已初始化数据:已初始化的全局变量、静态变量等
static int d;//数据段:未初始化数据:未初始化的全局变量、静态变量等
int e;
int f = 20;//栈:函数调用开销,比如局部变量。分配的内存空间地址越来越小
NSString *str = @"123”;//数据段:字符串常量:比如NSString *str = @"123"
NSObject *obj = [[NSObject alloc] init];//堆
:通过alloc、malloc、calloc等动态分配的空间,分配的内存空间地址越来越大
NSLog(@"\n&a=%p\n&b=%p\n&c=%p\n&d=%p\n&e=%p\n&f=%p\nstr=%p\nobj=%p\n",
&a, &b, &c, &d, &e, &f, str, obj);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
- 探究
/*
字符串常量
str=0x10dfa0068
已初始化的全局变量、静态变量
&a =0x10dfa0db8
&c =0x10dfa0dbc
未初始化的全局变量、静态变量
&d =0x10dfa0e80
&b =0x10dfa0e84
堆
obj=0x608000012210
栈
&f =0x7ffee1c60fe0
&e =0x7ffee1c60fe4
*/