iOS开发探索多线程GCD常用函数

目录
  • 正文
    • 单例
    • 栅栏函数
    • 调度组 dispatch_group_t
    • 信号量 dispatch_semaphore_t
    • dispatch_source
  • 总结

正文

前篇文章我们了解了GCD的任务的原理,接下来我们在探索一下GCD中我们开发常用的函数

单例

下面我们从源码中看一下我们创建单例的时候使用的dispatch_once,都做了什么,是通过什么操作保证全局唯一的

void dispatch_once(dispatch_once_t *val, dispatch_block_t block) {
    dispatch_once_f(val, block, _dispatch_Block_invoke(block));
}
void dispatch_once_f(dispatch_once_t *val, void *ctxt, dispatch_function_t func) {
    dispatch_once_gate_t l = (dispatch_once_gate_t)val;
    #if !DISPATCH_ONCE_INLINE_FASTPATH || DISPATCH_ONCE_USE_QUIESCENT_COUNTER
        uintptr_t v = os_atomic_load(&l->dgo_once, acquire);
        if (likely(v == DLOCK_ONCE_DONE)) {
            return;
        }
        #if DISPATCH_ONCE_USE_QUIESCENT_COUNTER
            if (likely(DISPATCH_ONCE_IS_GEN(v))) {
                return _dispatch_once_mark_done_if_quiesced(l, v);
            }
        #endif
    #endif
    if (_dispatch_once_gate_tryenter(l)) {
        return _dispatch_once_callout(l, ctxt, func);
    }
    return _dispatch_once_wait(l);
}
// 原子属性,比较 + 交换 &l->dgo_once 是否等于 DLOCK_ONCE_UNLOCKED,相等还没有执行过,将_dispatch_lock_value_for_self() 赋值给 &l->dgo_once,返回true;不等返回false
#define os_atomic_cmpxchg(p, e, v, m) \
    ({ _os_atomic_basetypeof(p) _r = (e); \
    atomic_compare_exchange_strong_explicit(_os_atomic_c11_atomic(p), \
    &_r, v, memory_order_##m, memory_order_relaxed); })
static inline bool _dispatch_once_gate_tryenter(dispatch_once_gate_t l) {
    return os_atomic_cmpxchg(&l->dgo_once, DLOCK_ONCE_UNLOCKED, (uintptr_t)_dispatch_lock_value_for_self(), relaxed);
}

和任务一样,对block进行了封装,同时通过判断dispatch_once_t *val的状态进行判断,DLOCK_ONCE_DONE直接返回,也就是已经执行了直接返回,调用_dispatch_once_gate_tryenter判断是否执行过block,未执行_dispatch_once_callout进行执行代码,执行过调用_dispatch_once_wait等待执行结束。

static void _dispatch_once_callout(dispatch_once_gate_t l, void *ctxt, dispatch_function_t func) {
    _dispatch_client_callout(ctxt, func); // 执行函数
    _dispatch_once_gate_broadcast(l); // 标记执行完成
}
static inline void _dispatch_once_gate_broadcast(dispatch_once_gate_t l) {
    dispatch_lock value_self = _dispatch_lock_value_for_self();
    uintptr_t v;
    #if DISPATCH_ONCE_USE_QUIESCENT_COUNTER
        v = _dispatch_once_mark_quiescing(l);
    #else
        v = _dispatch_once_mark_done(l); // 将 &l->dgo_once 设置成 DLOCK_ONCE_DONE,标记函数已经执行完成了,广播的作用
    #endif
    if (likely((dispatch_lock)v == value_self)) return;
    _dispatch_gate_broadcast_slow(&l->dgo_gate, (dispatch_lock)v);
}
void _dispatch_once_wait(dispatch_once_gate_t dgo) {
    ...省略部分...
    for (;;) {
        // 从底层获取 &dgo->dgo_onc 的状态
        os_atomic_rmw_loop(&dgo->dgo_once, old_v, new_v, relaxed, {
            if (likely(old_v == DLOCK_ONCE_DONE)) {
                os_atomic_rmw_loop_give_up(return); // 退出循环
            }
            #if DISPATCH_ONCE_USE_QUIESCENT_COUNTER
            if (DISPATCH_ONCE_IS_GEN(old_v)) {
                os_atomic_rmw_loop_give_up({
                    os_atomic_thread_fence(acquire);
                    return _dispatch_once_mark_done_if_quiesced(dgo, old_v);
                });
            }
            #endif
            new_v = old_v | (uintptr_t)DLOCK_WAITERS_BIT;
            if (new_v == old_v) os_atomic_rmw_loop_give_up(break);
        });
        ...省略部分...
    }
}

_dispatch_once_wait中使用死循环,直到DLOCK_ONCE_DONE时调用os_atomic_rmw_loop_give_up(return);退出循环,通过这种方式保证只创建一份.

栅栏函数

栅栏函数:前面的任务没有执行完成时,不执行栅栏函数中的任务,栅栏函数后面的任务需等待栅栏函数中的任务执行完成才能执行。

  • 栅栏函数只能拦截同一队列中的任务
  • 栅栏函数无法拦截全局队列,因为系统操作也会使用全局队列
  • 拦截同步队列和普通的任务原理相同 栅栏函数分为同步函数和异步函数,我们先看一下同步函数的源码dispatch_barrier_sync
void dispatch_barrier_sync(dispatch_queue_t dq, dispatch_block_t work) {
    ...省略...
    _dispatch_barrier_sync_f(dq, work, _dispatch_Block_invoke(work), dc_flags); // 和之前一样的封装block
}
_dispatch_barrier_sync_f -> _dispatch_barrier_sync_f_inline
static inline void _dispatch_barrier_sync_f_inline(dispatch_queue_t dq, void *ctxt, dispatch_function_t func, uintptr_t dc_flags) {
    dispatch_tid tid = _dispatch_tid_self();
    dispatch_lane_t dl = upcast(dq)._dl;
    if (unlikely(!_dispatch_queue_try_acquire_barrier_sync(dl, tid))) {
        return _dispatch_sync_f_slow(dl, ctxt, func, DC_FLAG_BARRIER, dl, DC_FLAG_BARRIER | dc_flags);
    }
    if (unlikely(dl->do_targetq->do_targetq)) {
        return _dispatch_sync_recurse(dl, ctxt, func, DC_FLAG_BARRIER | dc_flags);
    }
    _dispatch_introspection_sync_begin(dl);
    _dispatch_lane_barrier_sync_invoke_and_complete(dl, ctxt, func DISPATCH_TRACE_ARG(_dispatch_trace_item_sync_push_pop(dq, ctxt, func, dc_flags | DC_FLAG_BARRIER)));
}

在_dispatch_barrier_sync_f_inline函数中有3个执行func的函数, 我们将这三个函数使用符号断点来查看其调用的是哪个函数,发现调用的是_dispatch_sync_f_slow

static void _dispatch_sync_f_slow(dispatch_queue_class_t top_dqu, void *ctxt, dispatch_function_t func, uintptr_t top_dc_flags, dispatch_queue_class_t dqu, uintptr_t dc_flags) {
    dispatch_queue_t top_dq = top_dqu._dq;
    dispatch_queue_t dq = dqu._dq;
    if (unlikely(!dq->do_targetq)) {
        return _dispatch_sync_function_invoke(dq, ctxt, func);
    }
    ...省略部分...
    // 等待前面的任务执行完成
    _dispatch_trace_item_push(top_dq, &dsc);
    __DISPATCH_WAIT_FOR_QUEUE__(&dsc, dq);
    if (dsc.dsc_func == NULL) {
        // dsc_func being cleared means that the block ran on another thread ie.
        // case (2) as listed in _dispatch_async_and_wait_f_slow.
        dispatch_queue_t stop_dq = dsc.dc_other;
        return _dispatch_sync_complete_recurse(top_dq, stop_dq, top_dc_flags);
    }
    _dispatch_introspection_sync_begin(top_dq);
    _dispatch_trace_item_pop(top_dq, &dsc);
    _dispatch_sync_invoke_and_complete_recurse(top_dq, ctxt, func,top_dc_flags
}

继续查看_dispatch_sync_f_slow的源码, 内部执行func的函数有两个,也加入符号断点查看,最后得到调用的是_dispatch_sync_invoke_and_complete_recurse, 该方法就是同步任务调用的执行block任务的函数, 该函数内部有个_dispatch_sync_complete_recurse函数

static void _dispatch_sync_complete_recurse(dispatch_queue_t dq, dispatch_queue_t stop_dq, uintptr_t dc_flags){
bool barrier = (dc_flags & DC_FLAG_BARRIER);
    do {
        if (dq == stop_dq) return;
        if (barrier) {
            dx_wakeup(dq, 0, DISPATCH_WAKEUP_BARRIER_COMPLETE);
        } else {
            _dispatch_lane_non_barrier_complete(upcast(dq)._dl, 0);
        }
        dq = dq->do_targetq;
        barrier = (dq->dq_width == 1);
    } while (unlikely(dq->do_targetq));
}

该函数是使用do while,内部使用barrier来判断是否使用栅栏函数,没有栅栏函数就调用队列中接下来的任务,有栅栏函数就调用dx_wakeup

#define dx_wakeup(x, y, z) dx_vtable(x)->dq_wakeup(x, y, z)

dq_wakeup和之前异步任务的dq_push一样,是针对不同队列调用不同的方法

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_serial, lane,
    .do_type        = DISPATCH_QUEUE_SERIAL_TYPE,
    ......
    .dq_wakeup        = _dispatch_lane_wakeup,
);
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_concurrent, lane,
    .do_type        = DISPATCH_QUEUE_CONCURRENT_TYPE,
    ......
    .dq_wakeup        = _dispatch_lane_wakeup,
);
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_global, lane,
    .do_type        = DISPATCH_QUEUE_GLOBAL_ROOT_TYPE,
    ......
    .dq_wakeup        = _dispatch_root_queue_wakeup,
);
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_main, lane,
    .do_type        = DISPATCH_QUEUE_MAIN_TYPE,
    ......
    .dq_wakeup        = _dispatch_main_queue_wakeup,
);
// 串行队列及自己创建的并发队列
void _dispatch_lane_wakeup(dispatch_lane_class_t dqu, dispatch_qos_t qos, dispatch_wakeup_flags_t flags){
dispatch_queue_wakeup_target_t target = DISPATCH_QUEUE_WAKEUP_NONE;
    if (unlikely(flags & DISPATCH_WAKEUP_BARRIER_COMPLETE)) {
        // 栅栏函数任务完成后调用该函数
        return _dispatch_lane_barrier_complete(dqu, qos, flags);
    }
    if (_dispatch_queue_class_probe(dqu)) {
        target = DISPATCH_QUEUE_WAKEUP_TARGET;
    }
    // 唤醒后面队列中的任务,执行栅栏函数后面队列里的任务
    return _dispatch_queue_wakeup(dqu, qos, flags, target);
}
// 全局并发队列 没有任何关于栅栏函数的操作,所以栅栏函数对全局并发队列无效
void _dispatch_root_queue_wakeup(dispatch_queue_global_t dq, DISPATCH_UNUSED dispatch_qos_t qos, dispatch_wakeup_flags_t flags){
    if (!(flags & DISPATCH_WAKEUP_BLOCK_WAIT)) {
        DISPATCH_INTERNAL_CRASH(dq->dq_priority, "Don't try to wake up or override a root queue");
    }
    if (flags & DISPATCH_WAKEUP_CONSUME_2) {
        return _dispatch_release_2_tailcall(dq);
    }
}

我们可以看到全局并发队列的dq_wakeup关联的函数中没有任何关于栅栏函数的操作,所以栅栏函数对全局并发队列无效.

可是栅栏函数到底是怎么拦截的呢?

我们还是用全局符号断点来进行查看,将栅栏函数前面的任务进行延时操作,我们运行发现,调用_dispatch_sync_f_slow函数后,并没有立即调用_dispatch_sync_invoke_and_complete_recurse,而是等到我们前面的延时操作结束后,才进行的_dispatch_sync_invoke_and_complete_recurse调用, 也就是说在_dispatch_sync_f_slow函数调用后,会等待前面的函数全部执行完成后,才会执行栅栏函数本身的任务及唤醒栅栏函数后面的任务.

那为什么栅栏函数还区分同步和异步函数呢?

其实就是栅栏函数本身的任务是否需要开辟线程去进行执行来区分使用同步还是异步函数

调度组 dispatch_group_t

栅栏函数有自身的局限性,他只能拦截同一队列中的任务,当有多个队列的任务时,无法生效,这是我们应该使用调度组dispatch_group_t来进行任务拦截,保障任务的执行顺序. dispatch_group_t有两种书写方式:

dispatch_group_async(g, dispatch_get_global_queue(0, 0), ^{});

dispatch_group_enter(g); + dispatch_group_leave(g);

这两种书写方式,效果是一模一样的,dispatch_group_async就是dispatch_group_enter和dispatch_group_leave的整合

void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq, dispatch_block_t db){
    dispatch_continuation_t dc = _dispatch_continuation_alloc();
    uintptr_t dc_flags = DC_FLAG_CONSUME | DC_FLAG_GROUP_ASYNC;
    dispatch_qos_t qos;
    qos = _dispatch_continuation_init(dc, dq, db, 0, dc_flags);
    _dispatch_continuation_group_async(dg, dq, dc, qos);
}
static inline void _dispatch_continuation_group_async(dispatch_group_t dg, dispatch_queue_t dq, dispatch_continuation_t dc, dispatch_qos_t qos) {
    dispatch_group_enter(dg);
    dc->dc_data = dg;
    _dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
}
static inline void _dispatch_continuation_async(dispatch_queue_class_t dqu, dispatch_continuation_t dc, dispatch_qos_t qos, uintptr_t dc_flags) {
    #if DISPATCH_INTROSPECTION
        if (!(dc_flags & DC_FLAG_NO_INTROSPECTION)) {
            _dispatch_trace_item_push(dqu, dc);
        }
    #else
        (void)dc_flags;
    #endif
    return dx_push(dqu._dq, dc, qos);
}

dispatch_group_async其内部先进行dispatch_group_enter,然后和异步函数一样进行了dx_push调用来执行任务,和我们之前的异步任务是一样的流程,在_dispatch_root_queues_init_once-...->_dispatch_continuation_invoke_inline文件里我们可以看到dispatch_group_leave的调用

static inline void _dispatch_continuation_invoke_inline(dispatch_object_t dou, dispatch_invoke_flags_t flags, dispatch_queue_class_t dqu) {
    ...省略部分...
    if (unlikely(dc_flags & DC_FLAG_GROUP_ASYNC)) {
        _dispatch_continuation_with_group_invoke(dc);
    }
    ...省略部分...
}
static inline void _dispatch_continuation_with_group_invoke(dispatch_continuation_t dc) {
    struct dispatch_object_s *dou = dc->dc_data;
    unsigned long type = dx_type(dou);
    if (type == DISPATCH_GROUP_TYPE) {
        _dispatch_client_callout(dc->dc_ctxt, dc->dc_func);
        _dispatch_trace_item_complete(dc);
        dispatch_group_leave((dispatch_group_t)dou);
    } else {
        DISPATCH_INTERNAL_CRASH(dx_type(dou), "Unexpected object type");
    }
}

下面我们查看一下dispatch_group_enter、dispatch_group_leave的源码

void dispatch_group_enter(dispatch_group_t dg) {
    // The value is decremented on a 32bits wide atomic so that the carry
    // for the 0 -> -1 transition is not propagated to the upper 32bits.
    uint32_t old_bits = os_atomic_sub_orig2o(dg, dg_bits, DISPATCH_GROUP_VALUE_INTERVAL, acquire);
    uint32_t old_value = old_bits & DISPATCH_GROUP_VALUE_MASK;
    if (unlikely(old_value == 0)) {
        _dispatch_retain(dg); // <rdar://problem/22318411>
    }
    if (unlikely(old_value == DISPATCH_GROUP_VALUE_MAX)) {
        DISPATCH_CLIENT_CRASH(old_bits,
        "Too many nested calls to dispatch_group_enter()");
    }
}
void dispatch_group_leave(dispatch_group_t dg) {
    // The value is incremented on a 64bits wide atomic so that the carry for
    // the -1 -> 0 transition increments the generation atomically.
    // 修改 dg_state
    uint64_t new_state, old_state = os_atomic_add_orig2o(dg, dg_state, DISPATCH_GROUP_VALUE_INTERVAL, release);
    uint32_t old_value = (uint32_t)(old_state & DISPATCH_GROUP_VALUE_MASK);
    if (unlikely(old_value == DISPATCH_GROUP_VALUE_1)) {
        old_state += DISPATCH_GROUP_VALUE_INTERVAL;
        do {
            new_state = old_state;
            if ((old_state & DISPATCH_GROUP_VALUE_MASK) == 0) {
                new_state &= ~DISPATCH_GROUP_HAS_WAITERS;
                new_state &= ~DISPATCH_GROUP_HAS_NOTIFS;
            } else {
                // If the group was entered again since the atomic_add above,
                // we can't clear the waiters bit anymore as we don't know for
                // which generation the waiters are for
                new_state &= ~DISPATCH_GROUP_HAS_NOTIFS;
            }
            if (old_state == new_state) break;
        } while (unlikely(!os_atomic_cmpxchgv2o(dg, dg_state,  old_state, new_state, &old_state, relaxed)));
        return _dispatch_group_wake(dg, old_state, true);
    }
    if (unlikely(old_value == 0)) {
        DISPATCH_CLIENT_CRASH((uintptr_t)old_value,
        "Unbalanced call to dispatch_group_leave()");
    }
}

从源码中我们可以看到dispatch_group_enter进行减1操作,增加组内当前未完成任务的引用计数; dispatch_group_leave进行加1操作,减少组内未完成任务的引用计数;

当引用计数变成0,就会调用dispatch_group_notify来执行后续代码.

static inline void _dispatch_group_notify(dispatch_group_t dg, dispatch_queue_t dq, dispatch_continuation_t dsn) {
    uint64_t old_state, new_state;
    dispatch_continuation_t prev;
    dsn->dc_data = dq;
    _dispatch_retain(dq);
    prev = os_mpsc_push_update_tail(os_mpsc(dg, dg_notify), dsn, do_next);
    if (os_mpsc_push_was_empty(prev)) _dispatch_retain(dg);
    os_mpsc_push_update_prev(os_mpsc(dg, dg_notify), prev, dsn, do_next);
    if (os_mpsc_push_was_empty(prev)) {
        // 监听 dg_state
        os_atomic_rmw_loop2o(dg, dg_state, old_state, new_state, release, {
            new_state = old_state | DISPATCH_GROUP_HAS_NOTIFS;
            if ((uint32_t)old_state == 0) {
                os_atomic_rmw_loop_give_up({
                    return _dispatch_group_wake(dg, new_state, false);
                });
            }
        });
    }
}

我们可以看到dispatch_group_notify中会判断old_state == 0才会执行后续代码,如果dispatch_group_enter和dispatch_group_leave不是一一对应的则永远不会执行dispatch_group_notify,而且dispatch_group_leave中对old_value == 0,也进行了crash判断,dispatch_group_leave比dispatch_group_enter多的话会直接crash.

信号量 dispatch_semaphore_t

GCD中另一种常用的控制任务执行顺序的就是信号量,其主要是控制并发数量

  • 通过dispatch_semaphore_create(0)来创建信号量并指定信号的大小
  • dispatch_semaphore_signal(sem), 发送信号量,将信号量的值 +1
  • dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER) 等待信号量, 将信号量的值 -1, 当信号值的值小于0时会阻塞线程一直进行等待,当信号值的值大于等于0时执行后续代码

接下来我们还是查看源码来看看信号量的实现原理

intptr_t dispatch_semaphore_signal(dispatch_semaphore_t dsema) {
    // 对信号量的值 + 1
    long value = os_atomic_inc2o(dsema, dsema_value, release);
    if (likely(value > 0)) {
        return 0;
    }
    if (unlikely(value == LONG_MIN)) {
        DISPATCH_CLIENT_CRASH(value, "Unbalanced call to dispatch_semaphore_signal()");
    }
    return _dispatch_semaphore_signal_slow(dsema);
}
intptr_t _dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema) {
    _dispatch_sema4_create(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
    _dispatch_sema4_signal(&dsema->dsema_sema, 1);
    return 1;
}

我们可以看到signal就只有+1操作,下面我们主要看一下wait是如何进行等待的

intptr_t dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout) {
    // 对信号量进行减1操作
    long value = os_atomic_dec2o(dsema, dsema_value, acquire);
    // 当信号量的值 >= 0直接返回,执行后续代码
    if (likely(value >= 0)) {
        return 0;
    }
    return _dispatch_semaphore_wait_slow(dsema, timeout);
}
static intptr_t _dispatch_semaphore_wait_slow(dispatch_semaphore_t dsema, dispatch_time_t timeout){
    long orig;
    _dispatch_sema4_create(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
    switch (timeout) {
        default:
            if (!_dispatch_sema4_timedwait(&dsema->dsema_sema, timeout)) {
                break;
            }
        // Fall through and try to undo what the fast path did to
        // dsema->dsema_value
        case DISPATCH_TIME_NOW:
            orig = dsema->dsema_value;
            while (orig < 0) {
                if (os_atomic_cmpxchgv2o(dsema, dsema_value, orig, orig + 1, &orig, relaxed)) {
                    return _DSEMA4_TIMEOUT();
                }
            }
        // Another thread called semaphore_signal().
        // Fall through and drain the wakeup.
        case DISPATCH_TIME_FOREVER:
            _dispatch_sema4_wait(&dsema->dsema_sema);
            break;
    }
    return 0;
}
void _dispatch_sema4_wait(_dispatch_sema4_t *sema) {
    int ret = 0;
    do {
        ret = sem_wait(sema);
    } while (ret == -1 && errno == EINTR);
    DISPATCH_SEMAPHORE_VERIFY_RET(ret);
}

我们可以看到wait进行等待的函数 是根据我们传递的timeout进行的判断,然后进行do while循环阻塞当前线程获取信号量的值,如果信号量>=0跳出循环,执行后续代码.

  • 需要注意的是dispatch_semaphore_signal的数量不能比dispatch_semaphore_wait少,否则信号量内存无法被释放,会导致程序崩溃,_dispatch_semaphore_dispose函数中会进行判断当前的信号量的值 < 原始信号量的值 会触发崩溃.
void _dispatch_semaphore_dispose(dispatch_object_t dou, DISPATCH_UNUSED bool *allow_free) {
    dispatch_semaphore_t dsema = dou._dsema;
    if (dsema->dsema_value < dsema->dsema_orig) {
        DISPATCH_CLIENT_CRASH(dsema->dsema_orig - dsema->dsema_value, "Semaphore object deallocated while in use");
    }
    _dispatch_sema4_dispose(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
}

dispatch_source

dispatch_source 和runloop的source相同, 用于监听事件的,比如计时器,系统内存压力,mach port 待处理消息等.

dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0)) 创建事件源

dispatch_source_set_event_handler(source, ^{}) 设置源事件回调

dispatch_source_merge_data(source, 1) 设置源事件数据

dispatch_source_get_data(source) 获取源事件数据

dispatch_resume(source); 启动/继续

dispatch_suspend(source); 挂起,暂停

dispatch_source_cancel(source); 异步取消源事件

dispatch_cancel(source); 取消源事件

总结

栅栏函数拦截同一队列中的任务,无法拦截全局队列,因为系统操作也会使用全局队列,拦截同步队列就是普通的任务原理相同

调度组是通过未完成任务的引用计数来控制组里面任务的执行顺序

信号量其主要是控制并发数量,

以上就是iOS开发探索多线程GCD常用函数的详细内容,更多关于iOS开发多线程GCD函数的资料请关注我们其它相关文章!

(0)

相关推荐

  • iOS 多线程总结之GCD的使用详解

    进程与线程 进程就是一个应用程序在处理机上的一次执行过程,它是一个动态的概念,而线程是进程中的一部分,进程包含多个线程在运行. 线程是指进程内的一个执行单元,也是进程内的可调度实体. 与进程的区别: (1)地址空间:线程是进程内的一个执行单元;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间; (2)资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源 (3)线程是处理器调度的基本单位,但进程不是. (4)二者均可并发执行. GCD 1.什么是GCD? 全

  • iOS开发探索多线程GCD队列示例详解

    目录 引言 进程与线程 1.进程的定义 2.线程的定义 3. 进程和线程的关系 4. 多线程 5. 时间片 6. 线程池 GCD 1.任务 2.队列 3.死锁 总结 引言 在iOS开发过程中,绕不开网络请求.下载图片之类的耗时操作,这些操作放在主线程中处理会造成卡顿现象,所以我们都是放在子线程进行处理,处理完成后再返回到主线程进行展示. 多线程贯穿了我们整个的开发过程,iOS的多线程操作有NSThread.GCD.NSOperation,其中我们最常用的就是GCD. 进程与线程 在了解GCD之前

  • IOS开发之多线程NSThiread GCD NSOperation Runloop

    IOS中的进程和线程 通长来说一个app就是一个进程 ios开发中较少的运用进程间的通信(XPC),绝大多数使用线程. 在ios开发中,为了保证流畅性以及线程安全,所有与UI相关的操作都应该放在主线程,所以有时候主线程也叫UI线程. 影响UI体验,耗时时间较长的操作,尽量放到非主线程中.比如网络请求以及和本地的IO操作. 在IOS开发中有关于多线程的知识点主要包括:NSThread.GCD.NSOperation和Runloop NSThread NSthread就是一个线程,它的底层是对pth

  • iOS开发探索多线程GCD任务示例详解

    目录 引言 同步任务 死锁 异步任务 总结 引言 在上一篇文章中,我们探寻了队列是怎么创建的,串行队列和并发队列之间的区别,接下来我们在探寻一下GCD的另一个核心 - 任务 同步任务 void dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block); 我们先通过lldb查看其堆栈信息,分别查看其正常运行和死锁状态的信息 我们再通过源码查询其实现 #define _dispatch_Block_

  • iOS开发总结之UILabel常用属性介绍

    1.text:设置标签显示文本. 2.attributedText:设置标签属性文本. Ios代码 NSString *text = @"first"; NSMutableAttributedString *textLabelStr = [[NSMutableAttributedString alloc] initWithString:text]; [textLabelStr setAttributes:@{NSForegroundColorAttributeName : [UICol

  • iOS开发中多线程的安全隐患总结

    资源共享 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源 比如多个线程访问同一个对象.同一个变量.同一个文件 当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题 一.解决方案 解决方案:使用线程同步技术(同步,就是协同步调,按预定的先后次序进行) 常见的线程同步技术是:加锁 1.OSSpinLock OSSpinLock叫做"自旋锁",等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源 目前已经不再安全,可能会出现优先级反转问题 如果等待

  • 比较IOS开发中常用视图的四种切换方式

    在iOS开发中,比较常用的切换视图的方式主要有以下几种: 1. push.pop 使用举例(ViewController假设为需要跳转的控制器): [self.navigationController pushViewController:ViewController animated:YES]; //入栈,跳转到指定控制器视图 [self.navigationController popViewControllerAnimated:YES]; //弹栈,返回到前一个视图 [self.navig

  • IOS 多线程GCD详解

    Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法. dispatch queue分成以下三种: 1)运行在主线程的Main queue,通过dispatch_get_main_queue获取. #definedispatch_get_main_queue() \DISPATCH_GLOBAL_OBJECT(dispatch_queue_t, _dispatch_main_q) 可以看出,dispatch_get_main_queue也是一种disp

  • 详解iOS多线程GCD问题

    在iOS所有实现多线程的方案中,GCD应该是最有魅力的,因为GCD本身是苹果公司为多核的并行运算提出的解决方案.GCD在工作时会自动利用更多的处理器核心,以充分利用更强大的机器.GCD是Grand Central Dispatch的简称,它是基于C语言的.如果使用GCD,完全由系统管理线程,我们不需要编写线程代码.只需定义想要执行的任务,然后添加到适当的调度队列(dispatch queue).GCD会负责创建线程和调度你的任务,系统直接提供线程管理 dispatch queue分成以下三种:

  • IOS开发常用的正则表达式

    正则表达式是一种用来进行文本匹配的工具,其语法优美简洁.在开发中,查找.对比以及匹配字符串是家常便饭的业务,通过正则表达式我们将这些业务描述成某些需求规则,来让我们的代码更美观.实用.例如我们要验证用户输入的密码长度是否满足6~18位的长度,新手最常见的验证方式是判断输入的密码长度 return (textField.text.length >= 6 && textField.text.leng <= 18); 尽管这种判断方式没有任何问题,而上面的验证换做正则表达式的匹配字符

  • iOS开发中常用的各种动画、页面切面效果

    今天主要用到的动画类是CALayer下的CATransition至于各种动画类中如何继承的在这也不做赘述,网上的资料是一抓一大把.好废话少说切入今天的正题. 一.封装动画方法 1.用CATransition实现动画的封装方法如下,每句代码是何意思,请看注释之. #pragma CATransition动画实现 - (void) transitionWithType:(NSString *) type WithSubtype:(NSString *) subtype ForView : (UIVi

  • iOS 开发常用宏总结

    大家都是知道使用宏不仅方便,而且可以提高开发效率.下面总结了iOS开发过程中的一些常用宏,会持续的往里面添加. Objective-C //字符串是否为空 #define kStringIsEmpty(str) ([str isKindOfClass:[NSNull class]] || str == nil || [str length] < 1 ? YES : NO ) //数组是否为空 #define kArrayIsEmpty(array) (array == nil || [array

  • iOS开发中runtime常用的几种方法示例总结

    前言 Objective-C runtime是一个实现Objective-C语言的C库.它是一门编译型语言.也是一门动态型的语言(这里强调下OC是静态类型语言),之前没接触runtime的时候也不觉着它有多重要,接触之后才发现其实runtime挺强大的.就拿我们在iOS开发中所使用的OC编程语言来讲,OC之所以能够做到即是编译型语言,又能做到动态语言,就是得益于runtime的机制. 最近公司项目中用了一些 runtime 相关的知识, 初看时有些蒙, 虽然用的并不多, 但还是想着系统的把 ru

  • iOS开发中关键字const/static/extern、UIKIT_EXTERN的区别和用法

    一.前言 对于刚入行的新手们这些关键字可能会经常搞混淆或不清楚它们的意思和用法吧,即使在网上看了区别,但是很久不用下次又不清楚了,而且即使清楚自己的代码恐怕也很少用起来吧.通过阅读别人优秀的代码总会发现一些常用的关键字,随着自己的编程经验的积累慢慢的明白的. 二.关键字const/static/extern/UIKIT_EXTERN的释义和用法 1.const 这个单词翻译成中文是"常量"的意思.在程序中我们知道"常量"的值是不能变的,固定的.所以const关键字的

随机推荐

其他