swift语言AutoreleasePool原理及使用场景

目录
  • 使用场景
    • NSAutoreleasePool
    • @autoreleasepool
    • __autoreleasing
  • 源码分析
    • __AtAutoreleasePool结构体
    • AutoreleasePoolPage
    • POOL_BOUNDARY
  • 多层嵌套
    • push
    • autoreleaseFast
    • autoreleaseFullPage
    • autoreleaseNoPage
    • add
    • pop
    • popPage
    • releaseUntil
    • autorelease
    • hotPage
    • coldPage
  • 调试
    • _objc_autoreleasePoolPrint
  • UIApplicationMain
  • 释放时机
    • 区分
    • runloop

使用场景

ARC下,AutoreleasePool主要应用在大量创建临时对象的场景,通过AutoreleasePool控制内存峰值,是一个很好的选择。

NSAutoreleasePool

MRC可以调用NSAutoreleasePool使对象延迟释放,在ARC下这个API已经被禁用。

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// ...
[pool release];

@autoreleasepool

除了NSAutoreleasePool还可以使用@autoreleasepool,并且苹果推荐使用@autoreleasepool,因为这个API性能更好,在ARC下依然可以使用@autoreleasepool

无论是MRC还是ARCautorelease最大的作用,是在大量创建对象的同时,通过修饰让内存得到提前释放,从而降低内存峰值。

@autoreleasepool {
    NSMutableArray *channelItemsJSONArray = [NSMutableArray arrayWithContentsOfFile:[self cachedChannelItemsFile]];
    NSArray *items = [self channelItemsJSONArray];
    if (![items writeToFile:[self cachedChannelItemsFile] atomically:YES]) {
        [channelItemsJSONArray writeToFile:[self cachedChannelItemsFile] atomically:YES];
    }
    items = nil;
}

__autoreleasing

ARC下,需要被自动释放的对象,可以用__autoreleasing修饰,让对象延迟释放。

+ (NSArray *)parseString:(NSString *)originalM3U8Str m3u8Host:(NSString *)m3u8url error:(NSError *__autoreleasing *)errorPtr;

源码分析

__AtAutoreleasePool结构体

struct __AtAutoreleasePool {
    __AtAutoreleasePool() {
        atautoreleasepoolobj = objc_autoreleasePoolPush();
    }
    ~__AtAutoreleasePool() {
        objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
    void * atautoreleasepoolobj;
};

@autoreleasepool本质上会被系统转换成C++__AtAutoreleasePool结构体,@autoreleasepool的大括号开始,对应着objc_autoreleasePoolPush函数。大括号结束,对应着objc_autoreleasePoolPop函数。通过clang命令将OC代码转成C++代码,可以看到有一个__AtAutoreleasePool结构体。

__AtAutoreleasePool结构体在创建的时候会执行objc_autoreleasePoolPush函数,在释放的时候会执行析构函数,并执行objc_autoreleasePoolPop函数。在这两个函数内部,会调用AutoreleasePoolPagepushpop函数。

AutoreleasePoolPage

在运行时代码中,objc_autoreleasePoolPopobjc_autoreleasePoolPush,都调用了AutoreleasePoolPage类的实现。

void *
objc_autoreleasePoolPush(void)
{
    return AutoreleasePoolPage::push();
}
void
objc_autoreleasePoolPop(void *ctxt)
{
    AutoreleasePoolPage::pop(ctxt);
}

AutoreleasePoolPage的定义中,可以看到有parentchild的定义,当page中对象太多存储不下时,会创建其他的page对象来存储,AutoreleasePoolPage的结构是一个双向链表。在插入新的autorelease对象时,也会从链表头向后查找,直到找到未满的page

class AutoreleasePoolPage
{
    magic_t const magic;                // 校验page的结构是否完整
    id *next;                           // 指向下一个可以存放autorelease对象的地址
    pthread_t const thread;             // 当前所在的线程
    AutoreleasePoolPage * const parent; // 当前page的父节点
    AutoreleasePoolPage *child;         // 当前page的子节点
    uint32_t const depth;               // page的深度
    uint32_t hiwat;
}

AutoreleasePoolPage是一个C++的类,每个page4096个字节,也就是16进制的0x1000,也就是4kb的空间。这些空间中,其自身的成员变量只占56个字节,也就是下面七个成员变量,每个占8字节,总共56个字节。其他的四千多个字节,都用来存放被autorelease修饰的对象内存地址。

POOL_BOUNDARY

POOL_BOUNDARY的作用是,区分不同的自动释放池,也就是不同的@autoreleasepool。调用push时,会传入POOL_BOUNDARY并返回一个地址例如0x10380x1038是不存储@autorelease对象的地址的,起到一个标识作用,用来分割不同的@autoreleasepool

调用pop时,会传入end的地址,并从后到前调用对象的release方法,直到POOL_BOUNDARY为止。如果存在多个page,会从childpage的最末尾开始调用,直到POOL_BOUNDARYpage的结构是一个栈结构,释放的时候也是从栈顶开始释放。

next指针指向栈顶,是栈里面很常见的一个设计。AutoreleasePoolPagePOOL_BOUNDARY的区别在于,AutoreleasePoolPage负责维护存储区域,而POOL_BOUNDARY则负责分割存储在page中的对象地址,以@autoreleasepool为单位进行分割。

多层嵌套

@autoreleasepool {
    NSObject *p1 = [[NSObject alloc] init];
    NSObject *p2 = [[NSObject alloc] init];
    @autoreleasepool {
        NSObject *p3 = [[NSObject alloc] init];
        @autoreleasepool {
            NSObject *p4 = [[NSObject alloc] init];
        }
    }
}

如果是多层@autoreleasepool的嵌套,会用同一个AutoreleasePoolPage对象。以下面的三个嵌套为例,在同一个page中的顺序是下图这样。不同的@autoreleasepoolPOOL_BOUNDARY做分割。

push

创建一个autoreleasePool之后,就会调用push函数。在push函数中会判断是否调试模式下,如果调试模式会每次生成一个新的pagedebug环境代码可以直接忽略,只保留autoreleaseFast函数。

static inline void *push()
{
    id *dest;
    if (DebugPoolAllocation) {
        dest = autoreleaseNewPage(POOL_BOUNDARY);
    } else {
        dest = autoreleaseFast(POOL_BOUNDARY);
    }
    return dest;
}

autoreleaseFast

在函数内部,会通过hotPage获取当前的pagehotPage函数内部本质上是一个pagekey的映射。

  • 如果page不为空并且有空间,则调用pageadd函数将对象添加到page中,并将POOL_BOUNDARY添加在当前的位置。
  • 如果page已经被创建但没有空间,会调用autoreleaseFullPage函数创建新的page,并且将链表的末尾指向新创建的page
  • 如果没有创建page,则调用autoreleaseNoPage函数创建一个新的page,并且将当前线程的hotPage设置为新创建的page
static inline id *autoreleaseFast(id obj)
{
    AutoreleasePoolPage *page = hotPage();
    if (page && !page->full()) {
        return page->add(obj);
    } else if (page) {
        return autoreleaseFullPage(obj, page);
    } else {
        return autoreleaseNoPage(obj);
    }
}

autoreleaseFullPage

  • autoreleaseFullPage函数中,会从page的链表中,从前往后找到末尾的节点。
  • 创建一个新的page,在创建函数AutoreleasePoolPage中会处理parentchild指针的问题,返回的page可以直接用。
  • 调用setHotPagepage设置到哈希表中,并且调用pageadd函数将autorelease修饰的对象,添加到page中。
static __attribute__((noinline))
id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
{
    do {
        if (page->child) page = page->child;
        else page = new AutoreleasePoolPage(page);
    } while (page->full());
    setHotPage(page);
    return page->add(obj);
}

autoreleaseNoPage

autoreleaseNoPage函数的核心代码比较简单,就是创建一个新的page,随后设置POOL_BOUNDARY标志,并且把对象添加进去。在函数中需要留意POOL_BOUNDARY标志,很多地方都用来做page是否为空的判断。

static __attribute__((noinline))
id *autoreleaseNoPage(id obj)
{
    AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
    setHotPage(page);
    if (pushExtraBoundary) {
        page->add(POOL_BOUNDARY);
    }
    return page->add(obj);
}

add

add函数比较简单,核心逻辑就是将obj放入next指针的位置,并且对next指针进行++,指向下一个位置。*next++表示先用后加,先将obj存入next的地址,随后+1

id *add(id obj)
{
    ASSERT(!full());
    unprotect();
    id *ret = next;
    *next++ = obj;
    protect();
    return ret;
}

pop

调用pop函数时,有三步处理。

  • 判断autoreleasepool是否为空,通过EMPTY_POOL_PLACEHOLDER占位符判断,为空则清空这个page
  • 传入的stop是否不等于POOL_BOUNDARY标识,如果不等于则可能是一个有问题的page
  • 调用popPage方法,释放对象。
static inline void
pop(void *token)
{
    AutoreleasePoolPage *page;
    id *stop;
    // 1.
    if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
        page = hotPage();
        if (!page) {
            return setHotPage(nil);
        }
        page = coldPage();
        token = page->begin();
    } else {
        page = pageForPointer(token);
    }
    // 2.
    stop = (id *)token;
    if (*stop != POOL_BOUNDARY) {
        if (stop == page->begin()  &&  !page->parent) {
        } else {
            return badPop(token);
        }
    }
    // 3.
    return popPage<false>(token, page, stop);
}

popPage

popPage函数核心代码就是调用releaseUntil函数,在最开始会调用releaseUntil函数去完成释放操作。

按照page达到一半就扩容的原则,后面的if语句会判断执行poppage链表的状态。

如果少于半满,就将子节点删除。

如果大于半满,则保留子节点,并删除后面的节点。

static void
popPage(void *token, AutoreleasePoolPage *page, id *stop)
{
    page->releaseUntil(stop);
    if (page->child) {
        if (page->lessThanHalfFull()) {
            page->child->kill();
        }
        else if (page->child->child) {
            page->child->child->kill();
        }
    }
}

releaseUntil

releaseUntil函数内部,核心逻辑是从当前page,从后到前调用objc_release,释放被autorelease修饰的对象。

  • 获取当前的hotPage
  • 判断page是否为空,如果为空则表示里面的对象被释放完,则将page的父节点page设置为hotPage
  • 获得上一个节点,->的算数优先级比--要高,所以是先通过next获取当前节点地址,这是一个为空的待存入节点,随后执行--操作获取上一个对象地址。
  • 通过memset将上一个节点释放。
  • 判断上一个节点是否占位符号POOL_BOUNDARY,如果不是则调用objc_release释放对象。
  • while循环结束后,将当前page设置为hotPage
void releaseUntil(id *stop)
{
    while (this->next != stop) {
        AutoreleasePoolPage *page = hotPage();
        while (page->empty()) {
            page = page->parent;
            setHotPage(page);
        }
        page->unprotect();
        id obj = *--page->next;
        memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
        page->protect();
        if (obj != POOL_BOUNDARY) {
            objc_release(obj);
        }
    }
    setHotPage(this);
}

autorelease

对象调用autorelease方法会被编译器转换为objc_autoreleaseReturnValue方法,并且经过多层调用,会来到底层的autorelease函数。

在这个函数中会判断传入的对象是否tagged pointer,因为tagged pointer没有引用计数的概念。随后会调用autoreleaseFast函数,函数内部调用add函数将obj对象加入到page中,并且会判断是否需要创建新的page

static inline id autorelease(id obj)
{
    assert(!obj->isTaggedPointer());
    id *dest __unused = autoreleaseFast(obj);
    return obj;
}

hotPage

hotPage可以被理解为,page链表的末尾,也就是调用push函数被插入的位置。执行hotPage函数获取,以及调用setHotPage设置,都是操作的链表的末尾page

AutoreleasePoolPage对象和线程一一对应,并且都被存储在tls的哈希表中。通过tls_get_direct函数并传入key可以获取到对应的自动释放池。

static inline AutoreleasePoolPage *hotPage()
{
    AutoreleasePoolPage *result = (AutoreleasePoolPage *)
        tls_get_direct(key);
    if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil;
    if (result) result->fastcheck();
    return result;
}

hotPage函数中的判断是下面的定义,这个标示意思是当前page为空,也就是从未存储过任何对象。是一个标志位,下面是标志位的定义。

# define EMPTY_POOL_PLACEHOLDER ((id*)1)

coldPage

coldPage只有获取函数,没有设置函数。这是因为coldPage函数本质上,就是寻找page链表的根节点,从源码中的while循环可以看到。

static inline AutoreleasePoolPage *coldPage()
{
    AutoreleasePoolPage *result = hotPage();
    if (result) {
        while (result->parent) {
            result = result->parent;
            result->fastcheck();
        }
    }
    return result;
}

调试

_objc_autoreleasePoolPrint

如果想调试自动释放池,可以通过_objc_autoreleasePoolPrint私有API来进行。将项目改为MRC,并且在命令行项目中增加下面这些调试代码。

int main(int argc, const char * argv[]) {
    _objc_autoreleasePoolPrint();     // print1
    @autoreleasepool {
        _objc_autoreleasePoolPrint(); // print2
        Person *p1 = [[[Person alloc] init] autorelease];
        Person *p2 = [[[Person alloc] init] autorelease];
        _objc_autoreleasePoolPrint(); // print3
    }
    _objc_autoreleasePoolPrint();     // print4
    return 0;
}

打印结果如下,可以看到POOL_BOUNDARYpage中也占了一个位置。

objc[68122]: ############## (print1)
objc[68122]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[68122]: 0 releases pending. // 当前自动释放池中没有任何对象
objc[68122]: [0x102802000]  ................  PAGE  (hot) (cold)
objc[68122]: ##############
objc[68122]: ############## (print2)
objc[68122]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[68122]: 1 releases pending. // 当前自动释放池中有1个对象,这个对象为POOL_BOUNDARY
objc[68122]: [0x102802000]  ................  PAGE  (hot) (cold)
objc[68122]: [0x102802038]  ################  POOL 0x102802038  //POOL_BOUNDARY
objc[68122]: ##############
objc[68122]: ############## (print3)
objc[68122]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[68122]: 3 releases pending. // 当前自动释放池中有3个对象
objc[68122]: [0x102802000]  ................  PAGE  (hot) (cold)
objc[68122]: [0x102802038]  ################  POOL 0x102802038  //POOL_BOUNDARY
objc[68122]: [0x102802040]       0x100704a10  HTPerson          //p1
objc[68122]: [0x102802048]       0x10075cc30  HTPerson          //p2
objc[68122]: ##############
objc[68156]: ############## (print4)
objc[68156]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[68156]: 0 releases pending. // 当前自动释放池中没有任何对象,因为@autoreleasepool作用域结束,调用pop方法释放了对象
objc[68156]: [0x100810000]  ................  PAGE  (hot) (cold)
objc[68156]: ##############

UIApplicationMain

项目中经常会看到下面的代码,很多人的解释是“这个autoreleasepool是为了释放主线程的autorelease对象的”。但是,这个说法是错误的。autoreleasepool只负责自己作用域中添加的对象,而主线程在运行过程中,也会隐式创建autoreleasepool对象,这个pool是包含在main函数的pool里面的。

所以,主线程runloop每次执行循环后,释放的对象是主线程的。而main函数的autoreleasepool释放的,是main函数中直接创建的对象。

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

释放时机

区分

如果是在viewDidLoad方法中创建一个autorelease对象,并不是在这个方法结束后释放对象,这个说法是错误的。即便执行到viewDidAppear,依然不会释放对象。

autorelease修饰的对象,释放时机有两种。

  • 如果通过代码添加一个autoreleasepool,在作用域结束时,随着pool的释放,就会释放pool中的对象。这种情况是及时释放的,并不依赖于runloop
  • 另一种就是由系统自动进行释放,系统会在runloop开始的时候创建一个pool,结束的时候会对pool中的对象执行release操作。

runloop

如果是系统创建的pool,需要手动开启runloop,主线程默认已经开启并运行,子线程需要调用currentRunLoop方法开启并运行runloop,子线程中系统创建pool的流程才会正常工作。

包括主线程在内的每个线程,如果在线程中使用到了AutoreleasePool,则会创建两个Observer并添加到当前线程的Runloop中,通过这两个Observer进行对象的自动内存管理。

// activities = 0x1,kCFRunLoopEntry
<CFRunLoopObserver 0x60000012f000 [0x1135c2bb0]>{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x10eee6276)}
// activities = 0xa0,kCFRunLoopBeforeWaiting | kCFRunLoopExit
<CFRunLoopObserver 0x60000012ef60 [0x1135c2bb0]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x10eee6276)}

首先会创建一个Observer并监听kCFRunLoopEntry消息,时机是在进入Runloop前,此Observer的优先级设置为-2147483647的最高优先级,以保证回调发生在Runloop其他事件前。

然后创建另一个Observer,并监听kCFRunLoopBeforeWaitingkCFRunLoopExit消息,时机分别在进入Runloop休眠和退出Runloop时,将Observer的优先级设置为2147483647,以保证回调发生在Runloop其他事件之后。

两个Observer都有相同的回调函数_wrapRunLoopWithAutoreleasePoolHandler,在第一次回调时会在内部调用_objc_autoreleasePoolPush函数,创建自动释放池。

kCFRunLoopBeforeWaiting将要进入休眠前,调用_objc_autoreleasePoolPop函数释放自动释放池中的对象,并调用_objc_autoreleasePoolPush函数创建一个新的释放池。在kCFRunLoopExit将要退出Runloop时调用_objc_autoreleasePoolPop函数,释放自动释放池中的对象。

以上就是swift语言AutoreleasePool原理及使用场景的详细内容,更多关于swift AutoreleasePool 原理的资料请关注我们其它相关文章!

(0)

相关推荐

  • LeetCode 刷题 Swift 两个数组的交集

    目录 题目 方法一:两个集合 思路及解法 代码 复杂度分析 方法二:排序 + 双指针 思路及解法 代码 复杂度分析 题目 给定两个数组 nums1 和 nums2,返回 它们的交集 .输出结果中的每个元素一定是 唯一 的.我们可以 不考虑输出结果的顺序 . 示例 1: 输入: nums1 = [1,2,2,1], nums2 = [2,2] 输出: [2] 示例 2: 输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4] 输出: [9,4] 解释: [4,9] 也是可

  • LeetCode 题解 Swift 有效的完全平方数

    目录 题目 方法一:使用内置的库函数 思路及解法 复杂度分析 方法二:暴力 思路及解法 代码 复杂度分析 方法三:二分查找 思路及解法 细节 代码 复杂度分析 题目 给定一个 正整数 num,编写一个函数,如果 num 是一个完全平方数,则返回 true,否则返回 false. 进阶:不要 使用任何内置的库函数,如 sqrt. 示例 1: 输入: num = 16 输出: true 示例 2: 输入: num = 14 输出: false 方法一:使用内置的库函数 思路及解法 根据完全平方数的性

  • SwiftUI 引导页界面实现示例

    目录 引言 页面分析-元素构成 实战编程-创建项目 实战编程-引导图片 实战编程-引导文字 实战编程-引导按钮 实战编程-轮博滚动 整体效果-预览 本章小结 引言 当用户首次启用App时,客户端应用常常会出现一段过渡的App功能说明页面,帮助用户快速了解并熟悉App的基本功能和亮点. 引导页是用户了解产品的第一个窗口,能给用户留下最初的印象. 一个好的引导页可以很好地传达产品设计理念和产品设计调性,也是企业传达企业文化很好的窗口.当然对于开发者来说,也是必不可少的练手项目. 接下来,我们同样将用

  • swift语言Codable 用法及原理详解

    目录 Codable Codable 的用法 JSON 和 模型的相互转换 解码(JSON Data -> Model): 编码(Model -> JSON Data): Codable 支持的数据类型 基础数据类型 Date 嵌套对象 枚举 自定义 CodingKeys Codable 的原理 Decodable 协议 Container 核心原理分析(Container <--> JSON) JSONDecoder 的解码过程 编译器帮我们做了什么? 默认值问题 属性包装器 @

  • SwiftUI 登录界面布局实现示例详解

    目录 引言 页面分析-元素构成 实战编程-背景图片 实战编程-说明文字 实战编程-登录方式 实战编程-辅助文字 本章小结 引言 为了更好地了解和学习SwiftUI,我们快速学习SwiftUI的三种基本布局:HStack水平布局容器.VStack垂直布局容器.ZStack层叠布局容器. 在实际开发过程中,登录页面是移动端产品必不可少的页面,也是很好的练手项目. 接下来,我们将用10分钟来构建一个登录页面布局,以下面UI设计稿为例: 页面分析-元素构成 采用自顶向下的设计思想拆解UI设计稿的元素,可

  • Swift使用SnapKit模仿Kingfisher第三方扩展优化

    目录 正文 SnapKit扩展方式简要思考 Kingfisher扩展方式简要思考 自行模仿尝试 最后 正文 我们平时用swift写第三方扩展(OC中的分类)时,可能会直接就往扩展里面写方法,简单又方便,然而当我们看一些常用你的三方(例如:Kingfisher.SnapKit)等,都会用一个简单的参数引出(例如:kf.snp),下面来探索一下怎么用的,然后在总结其优缺点 SnapKit扩展方式简要思考 以 SnapKit为例,使用如下,发现引入了 snp var iv = UIImageView(

  • swift语言AutoreleasePool原理及使用场景

    目录 使用场景 NSAutoreleasePool @autoreleasepool __autoreleasing 源码分析 __AtAutoreleasePool结构体 AutoreleasePoolPage POOL_BOUNDARY 多层嵌套 push autoreleaseFast autoreleaseFullPage autoreleaseNoPage add pop popPage releaseUntil autorelease hotPage coldPage 调试 _obj

  • 简单分析Swift语言的一些基本特征

    Swift是苹果公司最新推出的编程语言,据很多人说,是用来"代替"Objective-C.但是没有确切的证据.我花了一些时间对Swift二进制和运行环境实施逆向工程技术,然后我对Swift有些少许的发现.目前为止,结论就是:Swift是没有消息机制的Objective-C. 对象 信不信由你,Swift中的对象就是Objective-C的对象.在Mach-O二进制文件中,__objc_classlist包含每个二进制文件中类的数据.其结构如下所示:   复制代码 代码如下: struc

  • ES6 Iterator遍历器原理,应用场景及相关常用知识拓展详解

    本文实例讲述了ES6 Iterator遍历器原理,应用场景及相关常用知识拓展.分享给大家供大家参考,具体如下: 介绍Iterator之前先列举下js的表示数据集合结构的几种方式: 在es6之前有 Array , Object, es6新增了 map, set,当然用户也可以组合使用这几种数据结构,灵活存储数据. 但是当数据结构变得复杂后,怎样取到里面的数据就也相对复杂,这就需要有一种读取数据的统一的接口机制,来处理不同的数据结构. 遍历器就是这样一种接口机制,Iterator是一种接口,为不同数

  • Go语言编译原理之变量捕获

    目录 前言 变量捕获概述 变量捕获底层实现 总结 前言 在前边的几篇文章中已经基本分享完了编译器前端的一些工作,后边的几篇主要是关于编译器对抽象语法树进行分析和重构,然后完成一系列的优化,其中包括以下五个部分: 变量捕获 函数内联 逃逸分析 闭包重写 遍历函数 后边的五篇文章主要就是上边这五个主题,本文分享的是变量捕获,变量捕获主要是针对闭包场景的,因为闭包函数中可能引用闭包外的变量,因此变量捕获需要明确在闭包中通过值引用或地址引用的方式来捕获变量 变量捕获概述 下边通过一个示例来看一下什么是变

  • Go语言底层原理互斥锁的实现原理

    目录 Go 互斥锁的实现原理? 概念 使用场景 底层实现结构 操作 加锁 解锁 Go 互斥锁正常模式和饥饿模式的区别? 正常模式(非公平锁) 饥饿模式(公平锁) Go 互斥锁允许自旋的条件? Go 互斥锁的实现原理? Go sync包提供了两种锁类型:互斥锁sync.Mutex 和 读写互斥锁sync.RWMutex,都属于悲观锁. 概念 Mutex是互斥锁,当一个 goroutine 获得了锁后,其他 goroutine 不能获取锁(只能存在一个写者或读者,不能同时读和写) 使用场景 多个线程

  • Swift语言与Applescript的区别?AppleScript的发展状况?

    本人为非开发人员(科研工作者),使用Mac,最近了解到Applescript对提高工作效率有很多帮助,希望学一下Applescript.不过最近10.10系统将发布,新的swift语言据说也能实现AppleScript的功能.我希望能了解Applescript最近的发展状况(Apple是否会逐渐淘汰这门语言),以及学习了swift是不是就不用学AppleScript了.希望高人解答,感谢大家 1.什么是applescript AppleScript是用在MacOSX上的脚本语言,和操作系统结合的

  • Swift语言实现地图坐标弹跳动画

    下面应用Swift语言做地图坐标实现弹簧跳动的效果,具体实现方法如下所示: iOS动画大概分这几种: CoreAnimation动画(基础)UIView动画(animateWithDuration开头的函数,带弹簧特效)UIDynamic动画(有物理引擎) 上次写弹跳动画,由于刚开始学,就用了最基础的CA动画,但是因为自己不会写KeyFrame,所以还引用了一个开源库来实现下坠的弹跳效果,这样固然是实现了需求,但是一点都不优雅--从iOS7开始,有了2.3两种动画,就不再需要用老的CA动画去一点

  • 深入解析Swift语言编程中的可选链

    查询,调用属性,下标和方法上的一个可选可能 'nil' 的过程被定义为可选的链.可选链返回两个值 如果可选包含一个值,然后调用其相关属性,方法和下标返回值 如果可选包含一个"nil"值,所有的相关属性,方法和下标返回nil 由于多种查询方法,属性和下标故障组合在一起,以一种链将影响到整个链,并导致产生 'nil' 的值. 可选链作为一种替代强制解包裹 可选链与可选值后指定"?"调用一个属性,方法或下标当可选的值返回一些值. 程序用于可选链 '!' 复制代码 代码如下

  • 深入理解Swift语言中的闭包机制

    在 Swift 中的闭包类似于结构块,并可以在任何地方调用,它就像 C 和 Objective C 语言内置的函数. 函数内部定义的常数和变量引用可被捕获并存储在闭包.函数被视为封闭的特殊情况,它有 3 种形式. 在 Swift 语言闭合表达式,如下优化,重量轻语法风格,其中包括: 推导参数并从上下文菜单返回值的类型 从单封表达的隐性返回 简略参数名称 尾部闭包语法 语法 下面是一个通用的语法定义用于闭包,它接受参数并返回数据的类型: 复制代码 代码如下: {(parameters) -> re

随机推荐