What's New in LLVM

作者:AloneMonkey,博客:www.blogfshare.com

What's New in LLVM

Xcode 9中LLVM编译器主要增加了如下几个方面的新功能。

  • OC/C/C++ Api可用性检查
  • 静态分析检查
  • 新的警告类型
  • C++重构
  • C++17的新特性
  • 链接时间优化

OC/C/C++ Api可用性检查

OC Api可用性检查

当使用新的api时,需要注意在老的系统上面运行可能会导致崩溃的问题,所以需要判断API运行的可用性。在Swift中,可以使用#availabl来判断,现在OC也可以了。

比如Base SDKiOS 11.0iOS Deployment TargetiOS 9.0,那么在使用iOS 11新增的Api时,比如:

[VNDetectFaceRectanglesRequest new]

就会提示警告:

那么可以通过运行时判断系统版本:

if(@available(iOS 11, *)){
    [VNDetectFaceRectanglesRequest new];
}else{

}

同样在编写SDK或者对外的接口的时候,某些方法或者类也只能在某个以上的版本才能使用,这个时候可以在方法和类上加上对应的可用版本号。

@interface MyAlbumController : UIViewController

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

@end
API_AVAILABLE(ios(11.0))
@interface MyAlbumController : UIViewController

-(void)showFaces;

@end

这样在调用者使用的时候,如果iOS Deployment Target低于指定的版本就会出现警告。

C/C++ Api可用性检查

C/C++也可以使用__builtin_available在运行时检测可用性。

if(__builtin_available(iOS 11, macOS 10.13, *)){
    CFNewAPIOniOS11();
}

或者定义对外接口的可用版本:

void myFunctionForiOS11OrNewer(int i) API_AVAILABLE(ios(11.0), macos(10.13));
class API_AVAILABLE(ios(11.0),macos(10.13)) MyClassForiOS11OrNewer{

};

在编译选项中可以设置是否检测、检测所有版本。

新的静态分析检查

新版增加了三个新的检测:

  • NSNumber和CFNumberRef中的比检测
  • dispatch_once实例唯一变量检测
  • NSMutable copy属性检测

NSNumber和CFNumberRef中的比较

如果在程序中,写出下面的代码:

@property NSNumber *photoCount;

-(BOOL)hasPhotos{
    return self.photoCount > 0;
}

其实这个时候语义就不太明确,是要比较photoCount是不是为nil,还是比较里面的数字是不是大于0。

所以使用Xcode的分析后就会报错警告:

这是改成如下代码,整数和整数的比较。

-(BOOL)hasPhotos{
    return self.photoCount.integerValue > 0;
}

同样,如下的代码也会有同样的问题。

需要明确程序的目的,比如这里是想判断faceCount是否为nil。

-(void)identifyFaces{
    if(self.faceCount != nil)
        return;
}

这可以在编译设置里面控制。

dispatch_once

在代码里面经常会用到dispatch_once保证某段代码只执行一次,来初始化获取唯一的实例。

+(NSArray<UIImage*>*)sharedPhotos{
    static NSArray<UIImage*> *sharedPhotos;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        sharedPhotos = [self loadPhotos];
    });
    return sharedPhotos;
}

这里很重要的一点就是oncePredicate必须是全局的或者静态变量,如果下是下面这种就不行了。

@implementation Album{
    dispatch_once_t oncePredicate;
}

dispatch_once(&oncePredicate, ^{
    self.photos = [self loadPhotos];
});

因为变量oncePredicate是可变的,获取到的地址也会不一样,所以Xcode会报如下警告:

也可以通过锁来保证同一个时间只有一个线程执行里面的代码。

@implementation Album{
    NSLock *photosLock;
}

[photosLock lock];
if(self.photos == nil){
    self.photos = [self loadPhotos];
}
[photosLock unlock];

copy

如果把NSMutableArray设为copy,如下:

@property (copy) NSMutableArray<UIImage*> *photos;

-(void)replaceWithStockPhoto:(UIImage*)stockPhoto{
    self.photos = [NSMutableArray<UIImage*> new];
    [self.photos addObject:stockPhoto];
}

当调用replaceWithStockPhoto:的时候,就会报错:

-[__NSArray0 addObject:]: unrecognized selector sent to instance 0x610000009ad0

因为给photos赋值的时候会调用copy变成不可变的NSArray,所以再去调用addObject的时候就会报错。

Xcode静态分析也会提醒:

当然可以重写setPhotos::

```
@synthesize photos = _photos;

-(void)setPhotos:(NSMutableArray *)photos{
_photos = [photos mutableCopy];
}

top Created with Sketch.