What's New in LLVM 9

ubuntu18.04安装llvm-9 clang-9 低版本的ubuntu只能采用编译安装的方式,高版本的ubuntu可以采用如下方式安装 echo deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-4.0 main\ >> /etc/apt/sources.list.d/llvm.list echo deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-4.0 main\ >> /etc/apt/ 阅读详情

What’s New in LLVM 9


这绝不仅仅是一篇 WWDC 2017 Session 411 学习笔记。除了有关 LLVM 9.0 的新特性之外,还有关于静态分析器和 Clang 5 Objective-C ARC 的一点看法和经验。
我觉得 LLVM 9 的亮点如下:

  1. 支持 Objective-C API 可用性检查
  2. 新增一些对隐患代码的静态分析检查和 warning
  3. 更快的 LTO,使其可以真正使用

API Availability Checking for Objective-C

在低版本系统上调用高版本 SDK 的 API 会引发 crash,所以在兼容多版本系统时需要谨慎判断系统版本,然后使用对应的 API。这些在运行时才能生效的代码很容易编写出错,而且需要测试人员付出很大工作量来覆盖到各个版本的系统。检查类、实例方法、类方法等是否可用时需要写的代码也不同,很蛋疼呦。
Swift 3 加入了 #available 语法来检查 API 是否可用,在编译阶段就可以发现哪里漏掉了 API 可用性检查。如今,Objective-C 也有这项功能啦。

Objective-C

假设加入 App 支持的最低版本是 iOS 10,但是直接调用了 iOS 11 的 API,那么编译器会告警,提醒开发者某个 API 只能用于较新版本的系统上。这时需要通过 @available 语法来判断平台和版本:

if(@available(iOS 11, *)) {
    // iOS 11 以上以及其他所有平台
}
  • 相当于通配符,代表所有其他平台都可用。

说完了如何检查 API 可用性后,再来谈谈声明 API 可用性的问题:

- (void)foo API_AVAILABLE(ios(11.0));

上面的代码声明了调用 foo 方法需要 iOS 11 以上,而 foo 方法内部的实现中调用 iOS 11 的 API 时无需再用 @available 检查。
声明一个类的可用性也很简单,并且无需给类中的每个方法再次声明可用性,只需要用到 API_AVAILABLE 宏:

API_AVAILABLE(ios(11.0))
@interface MyClassForiOS11OrNewer : NSObject
- (void)foo;
@end

其实类似的有关 API 兼容版本的宏还有好几个:

API_DEPRECATED()
API_UNAVAILABLE()
API_DEPRECATED_WITH_REPLACEMENT()

C & C++

如果是想在 C 或 C++ 中查询 API 的可用性,可以使用 LLVM 新加的 __builtin_available() 函数:

if (__builtin_available(iOS 11, macOS 10.13, *)) {
    // iOS 11 以上或 macOS 10.13 以上平台,以及其他所有平台
}

在 C 或 C++ 中使用 API_AVAILABLE 宏之前需要引入头文件

#include <os/availability.h>
class API_AVAILABLE(ios(11.0)) MyClassForiOS11OrNewer;

适用范围

对于旧工程,LLVM 只会对新的 API 进行告警(包含 iOS 11,tvOS 11,macOS 10.13,watchOS 4 以上)。旧的 API 不会被编译器告警,所以不用担心旧项目中已有的代码会产生一大片 warning,只需在采用新 API 的时候加上 @available 或 API_AVAILABLE。也可以选择在 Build Settings 中设置 Unguarded availability 为 YES(All Versions) 来检查所有的 API。Xcode 9 新建工程 Unguarded availability 项默认为 YES(All Versions)。

Static Analyzer Checks

除了在 Xcode->Product->Analyze 中开启静态分析检查外,也可以在 Build 过程中进行静态分析检查。只需在编译设置中将 Analyse During ‘Build’ 设为 YES。
苹果补充了一些检查项,看了下还都是一不留神就容易犯或者根本没注意到的细节。

比较 NSNumber

稍有经验的老司机都懂得 NSNumber 不能直接跟 raw value 直接比较,毕竟前者是类的实例对象,后者是基本类型。然而还是有人会弄错:

NSNumber *count = @0;
NSNumber *check = @YES;
if (count > 0) {
  NSLog(@"肯定会进到这里,因为 count 不为 nil");
}
if (check) {
  NSLog(@"肯定会进到这里,因为 check 不为 nil");
}

现在 LLVM 可以检查出这种情况,也可以关闭这项检查:在编译设置中将 Suspicious Conversions of NSNumber and CFNumberRef 设为 NO。

dispatch_once()

Xcode 的 code snippet 很好用,我觉得正常人不会把 dispatch_once() 写错吧。

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
   foo();
});

onceToken 只能是全局变量或静态变量,使得指针地址的唯一性保证了 foo() 只执行一次。但总有奇葩把 onceToken 声明成成员变量,使得指针地址可能会重复,无法保证线程安全地只执行一次。而现在 LLVM 可以检查出这种不规范的使用方式。

当然,想实现线程安全地只执行一次方法,也可以通过加锁和判断标志位的方式来实现。

NSMutable 类型的属性使用 copy 内存管理语义

@property (nonatomic, copy) NSMutableArray *array;

内存管理语义帮开发者自动合成了 set 方法的实现,但是当 copy 应用到了 NSMutable 系列的类上,就会产生问题。因为我们想要的是把新的值 mutableCopy 后赋值给属性,然而内存管理语义中只有 copy 却没有 mutableCopy。而现在 LLVM 可以检查出这种情况。

重写 set 方法可以解决此问题:

- (void)setArray:(NSMutableArray *)array
{
    _array = [array mutableCopy];
}

一些建议

静态分析能做到在编译阶段发现一些程序员容易疏忽的地方,它只能检查特定场景下的一些被认为不符合规范的行为。也就是说它维护了一个 List,编译的时候一项一项检查是否合符规范,但是这个 List 之外的行为并不能被检查出来,这也就是为什么 LLVM 每年都在向这个 List 新增内容。这个 List 包含了程序员容易犯的代码问题,并且这些问题暗涵很严重的逻辑错误。

  1. 随着 Check List 的不断膨胀,静态分析耗时会增加,对于大中型项目,我并不建议开启每次编译时都进行静态分析检查。建议在每个版本测试阶段定期做静态分析检查。
  2. 不要过于相信静态分析检查。首先它会漏检,Check List 之外的情况根本检查不出来。如果将一些 API 包含在宏定义中或者封装在 C 函数中调用,导致语法复杂,静态分析甚至还会误报。(我曾经碰见过几次静态分析检查出 MRC 下一些内存泄露,但其实间接调用了 autorelease 的,根本不会泄露,改为直接调用 autorelease 就 OK 了)
  3. 程序员自身应该有一份 Check List,在平时变成变成过程中不断约束自己。比如判断 NSString 是否有内容时直接看 length 是否大于 0,而不是判断是否为 nil 或 @”“。这跟打游戏是否有意识差不多,写代码也要有『意识流』。

New Warnings

Xcode 9 的 LLVM 又新增了一百多个 error 和 warning,然而大多数程序员还不是照样忽视 warning 么?可以在编译设置中将 warning 升级成 error。如果是旧的工程,需要升级工程文件到 Xcode 9,然后才能看到这些新增的 warning 设置项。(点击工程->Editor->Validate Settings)

ARC 中的 Block 捕获参数

- (void)validateDictionary:(NSDictionary<NSString *, NSString *> *)dict error:(NSError **)error
{
    [dict enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull obj, BOOL * _Nonnull stop) {
        if (obj.length > 0) {
            if (error) {
                *error = [NSError errorWithDomain:@"FishDomain" code:0 userInfo:nil];
            }
        }
    }];
}

ARC 会将 (NSError *)error 这种 “out-parameter” 隐式修饰为 __autoreleasing,也就是其实会被转化成 (NSError __autoreleasing *)error。(PS:相关知识参考 Indirect parameters)

给 *error 赋值时,因为它被 __autoreleasing 修饰,所以 ARC 下的 Clang 会隐式调用 retain 和 autorelease。 PS: 相关知识参考 Semantics

而 enumerateKeysAndObjectsUsingBlock: 方法会在 @autoreleasepool 里面执行 Block,在迭代逻辑中这样做有助于减少内存峰值。

于是 *error 就在 Block 里提前被释放了。Xcode 9 会针对这种情况发出 warning:”Block captures an autoreleasing out-parameter, which may result in use-after-free bugs”。解决方案:”Declare the parameter strong or capture a block __strong variable to keep values alive across autorelease pools”

第一个解决方案简单地把参数 (NSError *)error 改成 (NSError *__strong )error,这要求调用方也使用 ARC。第二个解决方案是利用 __block 让 Block 捕获外部变量,默认是强引用:

- (void)validateDictionary:(NSDictionary<NSString *, NSString *> *)dict error:(NSError **)error
{
    __block NSError *strongError = nil;
    [dict enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull obj, BOOL * _Nonnull stop) {
        if (obj.length > 0) {
            strongError = [NSError errorWithDomain:@"FishDomain" code:0 userInfo:nil];
        }
    }];
    if (error) {
        *error = strongError;
    }
}

关于 Clang 隐式调用 retain 和 autorelease 更深入的细节如下:

Clang 5 中的 Objective-C ARC 方法家族

Objective-C 中有五大方法家族,每个家族都有自己的规矩:alloc/new/copy/mutableCopy 四个家族的方法返回的是 “a retainable object pointer”,而 init 家族方法要求必须是实例方法,必须返回 Objective-C 对象。
把一个方法划归到某个方法家族有两种方式:

  1. 按照命名惯例划分方法家族
  2. 使用 attribute((objc_method_family( somefamily ))) 直接指定

如果某个方法不遵循所在家族的规矩,会影响到内存管理,造成内存泄露等后果。(PS:如果是使用第二种方式把不守规矩的方法划归到家族,Clang 会报错。)

ARC 下遵循一套内存管理原则:

  1. alloc/new/copy/mutableCopy 家族的方法创建的对象是自己持有的,会被 Clang 隐式标记为 attribute((ns_returns_retained))。等效于 Foundation 中的宏 NS_RETURNS_RETAINED
  2. init 家族会被 Clang 隐式标记为 attribute((ns_consumes_self)) 和 attribute((ns_returns_retained)),用 NS_REPLACES_RECEIVER 宏也是等效的。
    不属于任何方法家族的方法会被 Clang 隐式标记为 attribute((ns_returns_not_retained)),等效于 Foundation 中的宏 NS_RETURNS_NOT_RETAINED。

一般情况下 Clang 会帮我们做这些事情,不必给方法声明手动标记这些属性。而且 Clang 会对最终生成的汇编指令进行优化,去掉多余的 retain 和 autorelease。

ARC 会视情况在调用方法时可能会添加 retain,在方法内部返回时可能会添加 autorelease,经过优化后很可能会抵消。

attribute 其实并不是 Objective-C 的语法,而是 Clang 的属性。有关 Clang 的各种属性定义,请查看 Attributes in Clang

探索方法返回值内存管理的奥秘

先做两种情况的实验,查看汇编代码,并总结:方法为 alloc/new/copy/mutableCopy 家族或方法声明使用 NS_RETURNS_RETAINED

- (id)newFoo;
- (id)foo NS_RETURNS_RETAINED;

- (id)newFoo
{
    return [NSObject new];
}
- (id)foo
{
    return [NSObject new];
}

debug 时从汇编中可以看出:调用方无操作,方法返回时无操作。这显然是优化后的结果。虽然两种方式都能达到优化效果,但更推荐采用让方法加入方法家族,而不是直接使用 NS_RETURNS_RETAINED 宏。

普通方法

- (id)foo;

- (id)foo
{
    return [NSObject new];
}

debug 时从汇编中可以看出:调用方调用 objc_retainAutoreleasedReturnValue(),方法返回时调用 objc_autoreleaseReturnValue()(如果返回值没有赋值给变量,会调用 objc_unsafeClaimAutoreleasedReturnValue())。

优化过程及原理

objc_retainAutoreleasedReturnValue(value) 会尝试将之前调用 objc_autoreleaseReturnValue(value) 时传入参数 value 的所有权(retain count)传递过来。如果没有在 value 上调用过 objc_autoreleaseReturnValue(value),那就调用 retain。具体原理通过查看 Objective-C 源码可以得出(我列举的是 objc-709):

objc_autoreleaseReturnValue() 试图优化流程,如果上一层调用方会调用 objc_retainAutoreleasedReturnValue() 则直接返回参数,否则调用 autorelease:

id 
objc_autoreleaseReturnValue(id obj)
{
    // ReturnAtPlus1 表示倾向直接把对象返回,这样就不需要调用 objc_autorelease(),优化性能
    if (prepareOptimizedReturn(ReturnAtPlus1)) return obj;

    return objc_autorelease(obj);
}

static ALWAYS_INLINE bool 
prepareOptimizedReturn(ReturnDisposition disposition)
{
    assert(getReturnDisposition() == ReturnAtPlus0);
    // __builtin_return_address(0) 获取当前函数返回地址,传入 callerAcceptsOptimizedReturn 判断调用方是否紧接着调用了 objc_retainAutoreleasedReturnValue 或者 objc_unsafeClaimAutoreleasedReturnValue
    if (callerAcceptsOptimizedReturn(__builtin_return_address(0))) {
        // 将标志位 disposition 写入 TLS
        if (disposition) setReturnDisposition(disposition);
        return true;
    }

    return false;
}

static ALWAYS_INLINE void 
setReturnDisposition(ReturnDisposition disposition)
{// 向 TLS 中写入 Disposition
    tls_set_direct(RETURN_DISPOSITION_KEY, (void*)(uintptr_t)disposition);
}

static ALWAYS_INLINE ReturnDisposition 
getReturnDisposition()
{// 从 TLS 中读取 Disposition
    return (ReturnDisposition)(uintptr_t)tls_get_direct(RETURN_DISPOSITION_KEY);
}

callerAcceptsOptimizedReturn() 函数在不同架构的 CPU 上实现也是不一样的,这是因为不同架构 CPU 的对齐方式不同,偏移量也不同。比如在 arm64 上由于指令对齐方式较好,只需判断函数返回的地址指向的值是不是 0xaa1d03fd 即可;而在 x86_64 平台上则要用一大坨代码来判断。有兴趣的可以查看 objc-object.h 文件。

objc_retainAutoreleasedReturnValue() 试图查看是否已优化过并直接返回参数,否则 retain:

id
objc_retainAutoreleasedReturnValue(id obj)
{   // 如果之前 objc_autoreleaseReturnValue() 存入的标志位为 ReturnAtPlus1,则直接返回对象,无需调用 objc_retain(),优化性能
    if (acceptOptimizedReturn() == ReturnAtPlus1) return obj;

    return objc_retain(obj);
}

// 负责从 TLS 中取标志位 ReturnDisposition,然后将其重置
static ALWAYS_INLINE ReturnDisposition 
acceptOptimizedReturn()
{
    ReturnDisposition disposition = getReturnDisposition();
    setReturnDisposition(ReturnAtPlus0);  // reset to the unoptimized state
    return disposition;
}

TLS 全称为 Thread Local Storage,是每个线程专有的键值存储。在某个线程上的函数调用栈上相邻两个函数对 TLS 进行了存取,这中间肯定不会有别的程序『插手』。所以 getReturnDisposition() 和 setReturnDisposition() 的实现比较简单,不需要判断考虑是针对哪个对象的 Disposition 进行存取,因为当前线程上下文中只处理唯一的对象,保证不会乱掉。

无参数函数的声明

如果函数没有参数,需要用 void 显式声明。否则可能调用方会传入其他类型和数量的参数,在运行时引发 crash。如果用 void 显式声明,在编译阶段就会产生 error。

int foo() // warning:This function declaration is not a prototype
int foo(void) // OK

C++ Refactoring

对于一个带有 C++ 或 Objective-C++ 代码的工程来说,想重命名某个类名真的是艰难,Xcode 会提示不支持 C++!现在,各种操作也支持 C++ 咯:

LLVM 的重构代码功能极大节省了开发者的时间。

Features from C++17

其实这些都是 C++17 的新特性罢了,LLVM 9.0 积极响应支持。可以在编译选项 C++ Language Dialect 中选择使用的 C++ 标准库。GNU++17 比 C++17 多了语言扩展。

Structured Binding

解析 Tuple 可以一句搞定了:

std::tuple<int, double, char> compute();
void run() {
    auto [a, b, c] = compute();
}

甚至也可以解析类似 tuple 的类型:

struct Point { double x; double y; double z; };
Point computeMidPoint(Point p1, Point p2);
...
auto [x, y, z] = computeMidPoint(src, dest);

更多内容详见 C++17 的 Stuctured Binding

在条件判断语句中声明初始化变量

在判断某个条件之前,可能会产生一些中间变量,如果变量名与外界的有冲突,还会造成一些影响。所以需要一种只在条件判断语句范围内生效的局部变量,避免与无关逻辑代码有冲突:

if (auto a = getNumber(); a > 0) 
    foo();

...

a = 5; // error! a 只在上面的 if 作用域中有效。

constexpr if

以 if constexpr 开始的语句被称为 constexpr if 语句。– cppreference.com
举个栗子:advance 函数可以向前或向后迭代指定步数,但对于字符串和数组来说有更快的方式:无需一步步迭代,可以直接访问。但 advance 的参数是通用的,编译不通过:

传统解决方法是 Compile Time Dispatch:

C++17 的 constexpr if 可以一行搞定:

详见 constexpr if

string_view

简单来说它是指向字符串的指针,但不会拷贝一份字符串。一旦指向的字符串被修改或者被释放了,string_view 的内容也会跟跟着变,毕竟是同一份内存。所以,虽然会优化性能,慎用。就像 OC 中的字符串和数组传递赋值时一般都 copy,string_view 相当于是 assign,搞不好野指针呢。

详见 string_view

去年搞了个 LTO 和 增量 LTO,今年优化得编译速度更快了,于是建议我们打开 增量 LTO 啊:在编译选项中 Code Generation->Link-Time Optimization

其实我看了 2016 年的 What’s New in LLVM, LTO 确实占了很大篇幅,不过当时还不建议开启。经过一年的优化后算是修成正果了。

Windows下WSL2部署Hermes Agent全指南 Hermes Agent 是一个深度依赖 Linux 用户态环境、CUDA 加速与 POSIX 语义的本地 AI 助手框架,其核心组件(如 Ollama 调度器、Rust 向量化引擎、DeepSeek-R1 代理)在 Windows 原生环境中因缺乏 fork、epoll、/proc 文件系统及完整 CUDA ABI 兼容性而无法稳定运行。WSL2 凭借真实 Linux 内核、原生 GPU 支持(nvidia-smi 可见)和无缝 Windows 集成,成为当前唯一可行的部署底座。本指南聚焦 Ubuntu 阅读详情

相关推荐

LLVM(v9.0x64).7z

llvm v9.0 VS2017、x64、MT 编译。 无需VS运行库。

iOS 11 Xcode9开发 新特性学习 (警告篇)

最新版本SDK优化了开发体验,编译过程会提供更多提示警告,建议你修改。这些功能也可以自主选择用或者不用,当然,苹果喜欢你用他推荐的东西。。。 1 .@avalibale 语法,同步判断当前iOS系统是否满足需求。例如: if (@available(iOS 11, *)) { // >= 11 DLog(@"XXX1"); } else if (@av...

weixin_34290352的博客 348

LLVM-9.0.0-win64.zip

LLVM-9.0.0-win64压缩包。 LLVM是构架编译器(compiler)的框架系统,以C++编写而成,用于优化以任意程序语言编写的程序的编译时间(compile-time)、链接时间(link-time)、运行时间(run-time)以及空闲时间(idle-time),对开发者保持开放,并兼容已有脚本。

IOS 警告 收集

Semantic Warnings Warning Message -WCFString-literal input conversion stopped due to an input byte that does not belong to the input codeset UTF-8 -WNSObject-attri...

weixin_34283445的博客 383

What's New in LLVM9.0

What's New in LLVM By 杨萧玉  发表于 2017-07-17 文章目录 1. API Availability Checking for Objective-C 1.1. Objective-C1.2. C & C++1.3. 适用范围 2. Static Analyzer Checks 2.1. 比较 NSNumber2.2. 

mac的专栏 780

LLVM-9.0 学习者教程

LLVM-9.0 学习者教程 项目介绍 LLVM-9.0-Learner-Tutorial 是一个面向 LLVM(v9.0.0 或 v11.0.0)初学者的详细教程。该项目通过逐步讲解和详细的文档注释,记录了作者学习 LLVM 并最终完成一个完整项目的过程。教程的目标是帮助初学者理解和掌握 LLVM,特别是如何使用 LLVM 进行 FPGA 高层次综合(High-Level Synthesis, ...

gitblog_00450的博客 705

Linux内核llvm,LLVM 9.0 发布,能够构建Linux x86_64内核

它比计划晚了将近一个月,但是LLVM 9.0和Clang 9.0 C/C++编译器以及这个开源编译器基础架构的相关子项目今天已经发布了。LLVM 9.0是一个令人兴奋的版本,它带来了使用LLVM/Clang 9.0构建主线Linux x86_64内核的能力,现在终于添加了“asm goto”支持。 AArch64的支持以前更好,但现在很久以来主线Clang 9.0编译器可以构建当前的Linux内核...

weixin_42494628的博客 495

ubuntu14.04安装llvm/clang 9.0预编译版本

1 ubuntu14.04安装llvm/clang 9.0预编译版本 下载llvm/clang prebuild: https://releases.llvm.org/download.html#9.0.0 https://download.csdn.net/download/wwchao2012/12391517 xz -d clang+llvm-9.0.0-x86_64-linux-gnu-u...

2421

How to add a dark theme to your Qt application

Dark themes are now available for Windows 10 and Mac and it is increasingly expected that desktop applications will offer a dark theme. Previously Qt support for dark themes was patchy. But I am happy to say that it now seems to work fine with Qt 5

weixin_42389294的博客 499

gcc的__builtin_函数介绍

GCC提供了一系列的builtin函数,可以实现一些简单快捷的功能来方便程序编写,另外,很多builtin函数可用来优化编译结果。这些函数以“__builtin_”作为函数名前缀。 很多C标准库函数都有与之对应的GCC builtin函数,例如strcpy()有对应的__builtin_strcpy()内建函数。 下面就介绍一些builtin函数及其作用: __builtin

落尘纷扰的专栏 4万+

iOS消除对应的警告!

在iOS开发过程中, 我们可能会碰到一些系统方法弃用, weak、循环引用、不能执行之类的警告。 有代码洁癖的孩子们很想消除他们, 今天就让我们来一次Fuck 警告!!首先学会基本的语句 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" 这里写出现警告的代码#prag

贤_龍的博客 2352

__builtin函数总结

__builtin_clz(unsigned x); // count leading zeros __builtin_ctz(unsigned x); // count trailing zeros __builtin_popcount(unsigned x); // 1的个数 // ffs, parity 感觉没啥用。前者返回ctz+1,后者返回popcount&amp;1 // 另...

weixin_40676873的博客 4382

Xcode 9

原文地址:点击打开链接 Xcode 9 Xcode 9兼容性 Xcode 9需要Mac运行macOS 10.12.6或更高版本。 Xcode 9包括iOS 11,WatchOS 4,macOS 10.13和tvOS 11的SDK。 Xcode 9.0 一般 新功能 在导航和助手编辑器中添加了用于确定文件对应物的

疯狐狸的博客 2879

C++ 高效位运算函数 之 __builtin_

刷题时偶然发现了该系列函数,感觉不错,分享下 1.__builtin_popcount(n)该函数时判断n的二进制中有多少个1int n = 15; //二进制为1111 cout<<__builtin_popcount(n)<<endl;//输出42.__builtin_parity(n)该函数是判断n的二进制中1的个数的奇偶性int n = 15;//二进制为1111 int m = 7;//1

nobleman 8569

Robot Framework(十):BuiltIn中常用关键字(上)

目录 Log/Log Many Library Set Variable Set Suite Variable ​​​​​​​Catenate Create Dictionary ​​​​​​​Create List ​​​​​​​ BuiltIn是robot framework的标准库,提供了最通用和最常用的关键字,使用时会自动导入,下面我们只介绍使用频率较多的一些关键字,如果...

每天进步一点点 2669

ioS开发--Warning警告处理

去除警告的方法: #pragma clang diagnostic push #pragma clang diagnostic ignored"-Wunused-function" local void free_linkedlist(ll) linkedlist_data* ll; { free_datablock(ll->first_block); ll->first_blo

zzzzzdddddxxxxx的专栏 1万+

处理iOS开发中的各种警告

iOS开发中我们会遇到各种警告,包括第三方不再支持更新导致的警告,苹果一些过时方法的警告,其中尤其是ASIHttpRequest的不支持更新导致有很多的警告,所以我在这里做了罗列 Semantic Warnings WarningMessage -WCFString-literal input conversion stopped due to an inpu...

3155

LLVM的安装——从虚拟机到LLVM

2020年2月13日,好像是晴天也好像是阴天,管他什么天反正也没出门。晚上下楼扔垃圾,久违的微风拂面,恍然想起如今已经立春了,是春的温柔啊。啊,跑题了… 想要安装LLVM都需要准备些什么呢?需要安装:虚拟机、centos、gcc/g++、cmake。这里是我的安装步骤以及一些比较有参考性的文章。 准备步骤: 虚拟机的安装: 这个简单直接点点点就好没几步,装好后没有密钥的上网搜一个用就好,可以参...

CanaanLeeN的博客 1942
上一篇: GPUImage的图像形变设计(复杂形变部分)
下一篇: GPUImage源码解读(一)
Philm_iOS
博客等级 码龄8年 74粉丝 53原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值