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
 */
发布了29 篇原创文章 · 获赞 0 · 访问量 770
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览