1 OC调用Swift的方法报错如何解决?
- 在swift类中创建了方法:
@objc func changeViewColor(color: UIColor){
self.backgroundColor = color
}
- 在OC中调用
[test changeViewColor:[UIColor redColor]];
报错
No visible @interface for ‘TestSwiftView’ declares the selector ‘changeViewColor:’
原因
在Objective-C中方法名是 changeViewColorWithColor: 而不是changeViewColor: ,因为已经将 color: 暴露为外部标签
解决方案
用 changeViewColorWithColor: 调用
[test changeViewColorWithColor:[UIColor redColor]];
声明方法如下
@objc func changeViewColor(_ color: UIColor){
self.backgroundColor = color
}
为Objective-C调用声明方法
@objc(changeViewColor:) func changeViewColor(color: UIColor){
self.backgroundColor = color
}
注意点
Swift文件中使用OC类,在自动生成的桥接文件xxx-bridding-header.h中引入OC头文件
OC文件中使用Swift类:在需要使用的文件中引入#import”XXX-Swift.h”
2 iOS中静态库和动态库的区别?以及.a和framework的区别
什么是库
共享代码便是库,实现代码的复用,一般分为静态和动态库
静态库:链接时完整的拷贝到可执行文件,多次使用多次拷贝,造成冗余,使包变的更大
动态库:链接时不复制,程序运行时由系统加载内存中,供系统调用,系统加载一次多次共用,共同节省内存
iOS的静态库有.a和.framework
iOS的动态库有.dylib和.framework
为什么framework即是静态又是动态
系统的framework是动态的,我们自己创建的是静态的
.a和.framework的区别是什么
.a是单纯的二进制文件, .framwork是二进制文件+资源文件
其中.a不能直接使用,需要.h文件配合,而.framework则可以直接使用
.framework = .a + .h + sourceFile(资源文件)
为什么使用静态库
共享代码,方便使用
实现代码的模块化,固定的业务模块化,减少开发的重复劳动
和别人分享代码,但又不想让别人知道代码的具体实现
实现静态库的注意事项
- 无论是.a静态库还是.framework静态库,我们需要的都是二进制文件+.h+资源文件.不同的是,.a本身只是二进制文件,需要配上.h和资源文件才能使用,而.framework本身已经包含了二进制文件, .h和资源文件,可以直接使用
- 图片资源的处理:两种静态库,一般都是把图片文件单独的放在一个.bundle文件中,一般.bundle的名字和.a或者.framework的名字相同.新建一个文件夹,把它的名改为.bundle
- 把category打成静态库,但是在使用静态库的工程中,调用category中的方法时会有找不到改方法的运行时(selector not recognized),解决办法是:在使用静态库的工程中配置other linker flags的值为-ObjC
- 如果一个静态库很复杂,需要暴露的.h比较多的话,就可以在静态库的内部创建一个.h文件一般这个.h文件的名称和静态库的名字相同,然后把所有需要暴露出来的.h文件都集中放在这个.h文件中,而那些原本需要暴露的.h都不需要再暴露了,只需要把.h暴露出来就可以了
3 NSDictionary底层实现原理
在OC中NSDictionary是使用hash表来实现key和value的映射和存储的。
hash表存储过程简单介绍:
- 根据key值计算出它的hash值h;
- 假设箱子的个数是n,那么键值对应该放在第(h%n)个箱子中。
- 如果该箱子中已经有了键值对,就是用开放寻址法或者拉链法解决冲突。使用拉链法解决哈希冲突时,每个箱子其实是一个链表,属于同一个箱子的所有键值对都会排列在链表中。
OC中的字典其实是一个数组,数组中每一个元素同样为一个链表实现的数组,也就是数组中套数组。
在oc中每一个对象创建时,都默认生成一个hashCode,也就是经过hash算法生成的一串数字,当利用key去取字典中的value时,若是使用遍历或者二分查找等方法,效率相对较低,于是出现了根据每一个key生成的hashCode将键值对放到hasCode对应的数组中的指定位置,这样当用key去取值时,便不必遍历去获取,既可以根据hashCode直接取出。因为hashCode的值过大,或许经过取余获取一个较小的数字,假如是对999进行取余运算,那么得到的结果始终处于0-999之间。但是,这样做的弊端在于取余所得到的值,可能是相同的,这样可能导致完全不相干的键值对被新的键值对(取余后值key相等)所覆盖,于是出现了数组中套链表实现的数组。这样,key值取余得到值相等的键值对,都将保存在同一个链表数组中,当查找key对应的值时,首先获取到该链表数组,然后遍历数组,取正确的key所对应的值即可。
4 OC对象成员变量及方法如何存储
实例对象
typedef struct objc_class *Class ;
NSObject *obj =[ [NSObject alloc] init];
struct NSObject_IMPL {
Class isa; // 8个字节
}
// 结构体中只有一个指向objc_class结构体的一个指针,指针在64位中为8个字节
//获取NSObject实例对象的成员变量所占用的大小打印为 8
class_getInstanceSize([NSObject class])
//获取obj指针所指向内存的大小打印为16, OC规定最小对象占用16个字节,另外,内存对齐:结构体的最终大小必须是最大成员大小的倍数
malloc_size(obj)
@interface Student: NSObject
{ @public
int _no;
int _age;
}
@implementation Student
@end
/*
struct NSObject_IMPL {
Class isa; // 8个字节
}
struct Student_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _no;
int _age;
}
*/
Student *student = [[Student alloc]init];
总结:我们创建处理的OC的实例对象只放成员变量和isa指针(所有类都继承NSObject所以都有isa变量);成员变量的值是多少是有实例对象决定的比如age = 10,这个10就存储在实例对象中 (面试问题,成员变量的值存储在哪里?)
类对象与元类对象
NSObject *object = [[NSObject alloc] init];// 返回实例对象
Class objectClass1 = [object class];// 返回类对象
Class objectClass2 = [NSObject class]; // 返回类对象(这里注意下[[NSObject class] class]返回的还是类对象,调用多少遍class都是类对象,不是元类对象)
Class objectClass3 = object_getClass(object1);// 返回类对象
/* objectClass1,objectClass2,objectClass3都是NSObject的class类对象,他们是同一个类对象,每个类的内存中有且只有一个class对象;
class对象在内存中存储的信息包括
isa指针
superclass指针
类的属性信息(@property)
类的对象方法信息(instance method)
类的协议信息(protocol)
类的成员变量信息(ivar):这里说的不是成员变量的值(它是存储在实例对象中),是成员变量的类型(int),名字是age,成员变量的描述信息只是存储一份,因为大家的比如age都是Int类型,都叫age
*/
// 元类对象
Class objectMetaClass = object_getClass([NSObject class);// 参数为类对象,返回值就是元类对象
/*
Class object_getClass(id obj){
if(obj) return obj->getIsa();
};//如果传进来的obj为instance对象,返回class对象,如果是class对象,返回meta-class对象,如果传进来是meta-class对象,就返回NSObject(基类)的meta-class对象
Class objc_getClass(const char *aClassName){ // 传一个类名字符串 返回的是一个类对象
return look_up_class(aClassName);
}
meta-class对象在内存中存储的信息包括
isa指针
superclass指针
类的类方法信息(class method)
*/
总结1:
- instance的isa指向class 当调用对象方法时,通过instance的isa找到class,最后找到对象方法的实现进行调用)
- class的isa指向meta-class 当调用类方法时,通过class的isa找到meta-class,最后找到类方法的实现进行调用
- class对象的superclass指针 当Student的instance对象要调用Person的对象方法时,会先通过isa找到Student的class,然后通过superclass找到Person的class,最后找到对象方法的实现进行调用
- meta-class对象的superclass指针 当Student的class要调用Person的类方法时,会先通过isa找到Student的meta-class,然后通过superclass找到Person的meta-class,最后找到类方法的实现进行调用
- (类对象的superClass指向的是父类的类对象,元类对象的superClass指针指向的是父类的元类对象)
isa、superclass总结
- instance的isa指向class
- class的isa指向meta-class
- meta-class的isa指向基类的meta-class
- class的superclass指向父类的class 如果没有父类,superclass指针为nil
- meta-class的superclass指向父类的meta-class 基类的meta-class的superclass指向基类的class
- instance调用对象方法的轨迹 isa找到class,方法不存在,就通过superclass找父类
- class调用类方法的轨迹 isa找meta-class,方法不存在,就通过superclass找父类
5 category为什么不能存储成员变量
这要从Category的原理说起,简单地说就是通过runtime动态地把Category中的方法等添加到类中(苹果在实现的过程中并未将属性添加到类中,所以属性仅仅是声明了setter和getter方法,而并未实现),具体请参考http://tech.meituan.com/DiveIntoCategory.html
在Objective-C提供的runtime函数中,确实有一个lass_addIvar()
函数用于给类添加成员变量,但是文档中特别说明:
This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported.
意思是说,这个函数只能在“构建一个类的过程中”调用。一旦完成类定义,就不能再添加成员变量了。经过编译的类在程序启动后就被runtime加载,没有机会调用addIvar。程序在运行时动态构建的类需要在调用objc_registerClassPair
之后才可以被使用,同样没有机会再添加成员变量。
我们设想一下如果Objective-C允许动态增加成员变量,会发生什么事情。假设如下代码可以执行。
/*
NSObject
Class isa
*/
/*
MyObject: NSObject
0 Class isa
4 NSArray students
8 NSArray teachers
*/
MyObject *obj = [[MyObject alloc] init];
// 基类增加一个4字节的成员变量someVar
class_addIvar([NSObject class], "someVar", 4, ...);
// 基类增加方法someMethod,用到了someVar
class_addMethod([NSObject class], @selector(someMethod), ...);
// 调用someMethod,修改了someVar
[obj someMethod];
// 访问子类成员变量,会发生什么?
[obj->students length];
显然,这样做会带来严重问题,为基类动态增加成员变量会导致所有已创建出的子类实例都无法使用,比如上线后的app,如果用户手机系统升级iOS新版本后,必须重新编译提交才能在新版系统上运行。那为什么runtime允许动态添加方法和属性,而不会引发问题呢?
因为方法和属性并不“属于”类实例,而成员变量“属于”类实例。我们所说的“类实例”概念,指的是一块内存区域,包含了isa指针和所有的成员变量。所以假如允许动态修改类成员变量布局,已经创建出的类实例就不符合类定义了,变成了无效对象。但方法定义是在objc_class中管理的,不管如何增删类方法,都不影响类实例的内存布局,已经创建出的类实例仍然可正常使用。
需要注意的有两点:
- category的方法没有“完全替换掉”原来类已经有的方法,也就是说如果category和原来类都有methodA,那么category附加完成之后,类的方法列表里会有两个methodA
- category的方法被放到了新方法列表的前面,而原来类的方法被放到了新方法列表的后面,这也就是我们平常所说的category的方法会“覆盖”掉原来类的同名方法,这是因为运行时在查找方法的时候是顺着方法列表的顺序查找的,它只要一找到对应名字的方法,就会罢休_,殊不知后面可能还有一样名字的方法。
注意3
// 创建类
Class newClass = objc_allocateClassPair([NSObject class], "Dog", 0);
// 添加成员变量
class_addIvar(newClass,"_age", 4, 1, @encode(int));
// 添加方法
class_addMethod(newClass, @selector(run),(IMP)run,"v@:");
// 注册类
objc_registerClassPair(newClass);
那么可以将添加成员变量放在注册类之后么? 答案是不可以的
struct class_rw_t {const class_ro_t *ro}; struct class_ro_t {const ivar_list_t *ivars;//成员变量列表}
成员变量是只读的,所以类的结构一旦确定了,是不能去动态添加成员变量的,所以class_addIvar是不能给已经存在的类添加成员变量的,因为已经存在的代码已经订好了,只要动态创建且还未注册的类才能添加成员变量,结构还没有定向之前可以.
可以将添加方法放在注册类之后
struct class_rw_t {method_list_t *methods};
方法是可读写的,可以动态添加
6 OC 对象的内存存储结构
7 iOS 企业账号、公司账号、个人账号之间的区别
1 个人(Individual)
(1)费用:99美元一年(2)App Store上架:是(3)最大udid支持数:100
(4)协作人数:1人(开发者自己)(5)该账号在App Store销售者只能显示个人的ID,比如bai ji
说明:“个人”开发者可以申请升级“公司”
2 公司(Company):
(1)费用:99美元一年(2)App Store上架:是(3)最大udid支持数:100
(4)协作人数:多人(5)该账号该账号在App Store销售者可以显示类似Studios,或者自定义的团队名称
(6)分4种管理级别权限:
Admin Legal权限:超级管理员。可以管理开发者和管理app store中的应用。
Admin权限:管理员,可以管理开发者。添加测试机子和管理团队证书。
Member权限:是普通开发者。只能下载证书和使用证书
No Access权限:没有相应的权限。
(7)公司账号可以自己定义一定数量的开发者子账号,不过只能由主账号来执行提交,发布等操作。
说明:允许多个开发者进行协作开发,比个人多一些帐号管理的设置,可设置多个Apple ID,分4种管理级别的权限。公司帐号比个人多一些帐号管理的设置:个人帐号集Agent,Admin,Developer等为一身,而公司帐号可以设置不同的AppleID来担当。
说明:申请时需要填写公司的邓白氏编码(DUNS Number)。
申请公司团队账号地址
邓白氏编码(D-U-N-S)
3 企业 (Enterprise)
1)费用:299美元一年 (2)App Store上架:否
即该账号开发应用不能发布到App Store,只能企业内部应用,苹果的iOS设备UDID数量不限制。
(3)最大udid支持数:不限制 (4)协作人数:多人
(5)企业开发者不能通过appstore途径发app,但是可以直接无上限的分发
app(in-house 发布)
说明:企业账号不能上线应用到App Store,适用于那些不希望公开发布应用的企业且还需要大量安装使用的公司。同样,申请时也需要公司的邓白氏编码(DUNS Number)。
8 线程和线程之间如何通信
1 .一个线程传递数据给另一个线程
2 .在一个线程中执行完特定任务后,转到另一个线程继续执行任务
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
用法如下:
//点击屏幕开始执行下载方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self performSelectorInBackground:@selector(download) withObject:nil];
}
//下载图片
- (void)download
{
// 1.图片地址
NSString *urlStr = @"http://d.jpg";
NSURL *url = [NSURL URLWithString:urlStr];
// 2.根据地址下载图片的二进制数据
NSData *data = [NSData dataWithContentsOfURL:url];
NSLog(@"---end");
// 3.设置图片
UIImage *image = [UIImage imageWithData:data];
// 4.回到主线程,刷新UI界面(为了线程安全)
[self performSelectorOnMainThread:@selector(downloadFinished:) withObject:image waitUntilDone:NO];
// [self performSelector:@selector(downloadFinished:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
}
- (void)downloadFinished:(UIImage *)image
{
self.imageView.image = image;
NSLog(@"downloadFinished---%@", [NSThread currentThread]);
}
GCD
一个线程传递数据给另一个线程,如:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"donwload---%@", [NSThread currentThread]);
// 1.子线程下载图片
NSURL *url = [NSURL URLWithString:@"http://d.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
// 2.回到主线程设置图片
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"setting---%@ %@", [NSThread currentThread], image);
[self.button setImage:image forState:UIControlStateNormal];
});
});
}
9 Tagged Pointer的isa如何访问
Tagged Pointer的引入也带来了问题,即Tagged Pointer因为并不是真正的对象,而是一个伪对象,所以你如果完全把它当成对象来使,可能会让它露马脚。比如我在 《Objective-C 对象模型及应用》 一文中就写道,所有对象都有 isa 指针,而Tagged Pointer其实是没有的,因为它不是真正的对象。
因为不是真正的对象,所以如果你直接访问Tagged Pointer的isa成员的话,在编译时将会有如下警告:
对于上面的写法,应该换成相应的方法调用,如 isKindOfClass 和 object_getClass。只要避免在代码中直接访问对象的 isa 变量,即可避免这个问题。
总结
苹果将Tagged Pointer引入,给 64 位系统带来了内存的节省和运行效率的提高。Tagged Pointer通过在其最后一个 bit 位设置一个特殊标记,用于将数据直接保存在指针本身中。因为Tagged Pointer并不是真正的对象,我们在使用时需要注意不要直接访问其 isa 变量。
10 AFN弃用UIWebView
11 删除倒数第n个节点(双指针)
12 闭包为什么和强大
13 mach-O
14 程序crash定位到main函数怎么办
xcode只要装简单设置一下,就能准确给出堆栈信息了, 打开xcode左边的Breakpoint Navigator ,点击下面的+号添加一个Exception Breakpoint,可以选择 All, 然后再运行试试,Crash后,就会在在抛出异常的时候停下来了
如何通过Xcode来找到崩溃在Main入口函数的原因??
很多这种情况是已经释放的对象再调用其方法就会产生EXC_BAD_ACCESS这样的报错
Xcode自带Zombie检测的开关
第一:点这里,选择edit scheme,勾选zombile Object;
15 启动优化
16 多线程
fileprivate func loadDataRef() {
let group = DispatchGroup();
let queue = DispatchQueue.global()
let semaphore = DispatchSemaphore(value: 0);
queue.async(group: group, qos: .default, flags: []) {
print("begin 1111111111111111111111111111")
do {
let url = URL(string: "https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/topnav/yinyue@2x-c18adacacb.png")
let imagData = try Data.init(contentsOf: url!)
let image = UIImage(data: imagData)
print(image)
print("1111111111111111111111111111111111")
} catch {
return
}
print("end 1111111111111111111111111111")
}
queue.async(group: group, qos: .default, flags: []) {
print("begin 222222222222222222222222222")
do {
let url = URL(string: "https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/topnav/yinyue@2x-c18adacacb.png")
let imagData = try Data.init(contentsOf: url!)
let image = UIImage(data: imagData)
print(image)
print("222222222222222222222222222")
} catch {
return
}
print("end 222222222222222222222222222")
}
queue.async(group: group, qos: .default, flags: []) {
print("begin 333333333333333333333333")
do {
let url = URL(string: "https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/topnav/yinyue@2x-c18adacacb.png")
let imagData = try Data.init(contentsOf: url!)
let image = UIImage(data: imagData)
print(image)
print(" 333333333333333333333333")
} catch {
return
}
print("end 333333333333333333333333")
group.notify(queue: queue) {
print("回到该队列中执行")
}
print("完成")
}
/* 打印结果
完成
begin 1111111111111111111111111111
begin 222222222222222222222222222
begin 333333333333333333333333
Optional(<UIImage:0x2812633c0 anonymous {88, 88}>)
1111111111111111111111111111111111
end 1111111111111111111111111111
Optional(<UIImage:0x2812633c0 anonymous {88, 88}>)
222222222222222222222222222
end 222222222222222222222222222
Optional(<UIImage:0x281266520 anonymous {88, 88}>)
333333333333333333333333
end 333333333333333333333333
回到该队列中执行
*/
fileprivate func loadDataRef() {
let group = DispatchGroup()
let queue = DispatchQueue.global()
print("mmmmmmmmmmmmmmmmm")
group.enter()
queue.async(group: group, qos: .default, flags: []) {
print("begin 111111111111")
FolderNetWork.literatureRequest(.getReferenceAndCitationLitera(dbCode: dbCode, tableName: tableName, fileName: fileName, type: referenceType, Page: page), success: { (result) in
print("11111")
group.leave()
}, error: { (errorCode) in
print("11111")
}, failure: { (failureDes) in
print("11111")
})
print("end 111111111111")
}
group.enter()
queue.async(group: group, qos: .default, flags: []) {
print("begin 2222222222")
FolderNetWork.literatureRequest(.getReferenceAndCitationLitera(dbCode: dbCode, tableName: tableName, fileName: fileName, type: "3", Page: "1"), success: { (result) in
print("2222222222")
group.leave()
}, error: { (errorCode) in
print("2222222222")
}, failure: { (failureDes) in
print("2222222222")
})
print("end 2222222222")
}
group.enter()
queue.async(group: group, qos: .default, flags: []) {
print("begin 3333333")
FolderNetWork.literatureRequest(.getPaperInfo(dbCode: dbCode, tableName: tableName, fileName: fileName, type: referenceType, Page: page), success: { (result) in
print("3333333")
group.leave()
}, error: { (errorCode) in
print("3333333")
}) { (failureDes) in
print("3333333")
}
print("end 3333333")
}
group.notify(queue: queue) {
print("回到该队列中执行")
}
print("完成")
}
/*打印结果
mmmmmmmmmmmmmmmmm
完成
begin 111111111111
end 111111111111
begin 2222222222
end 2222222222
begin 3333333
end 3333333
https://*****, bodyEncoding: Alamofire.JSONEncoding(options: __C.NSJSONWritingOptions(rawValue: 0)), urlParameters: [:])
https://*****, bodyEncoding: Alamofire.JSONEncoding(options: __C.NSJSONWritingOptions(rawValue: 0)), urlParameters: [:])
https://*****, bodyEncoding: Alamofire.JSONEncoding(options: __C.NSJSONWritingOptions(rawValue: 0)), urlParameters: [:])
2222222222
https://*****, bodyEncoding: Alamofire.JSONEncoding(options: __C.NSJSONWritingOptions(rawValue: 0)), urlParameters: [:])
3333333
https://*****, bodyEncoding: Alamofire.JSONEncoding(options: __C.NSJSONWritingOptions(rawValue: 0)), urlParameters: [:])
11111
回到该队列中执行
*/
fileprivate func loadDataRef() {
let group = DispatchGroup();
let queue = DispatchQueue.global()
let semaphore = DispatchSemaphore(value: 0);
print("mmmmmmmmmmmmmmmmm")
queue.async(group: group, qos: .default, flags: []) {
print("begin 111111111111")
FolderNetWork.literatureRequest(.getReferenceAndCitationLitera(dbCode: dbCode, tableName: tableName, fileName: fileName, type: referenceType, Page: page), success: { (result) in
print("11111")
}, error: { (errorCode) in
print("11111")
}, failure: { (failureDes) in
print("11111")
})
print("end 111111111111")
}
queue.async(group: group, qos: .default, flags: []) {
print("begin 2222222222")
FolderNetWork.literatureRequest(.getReferenceAndCitationLitera(dbCode: dbCode, tableName: tableName, fileName: fileName, type: "3", Page: "1"), success: { (result) in
print("2222222222")
}, error: { (errorCode) in
print("2222222222")
}, failure: { (failureDes) in
print("2222222222")
})
print("end 2222222222")
}
queue.async(group: group, qos: .default, flags: []) {
print("begin 3333333")
FolderNetWork.literatureRequest(.getPaperInfo(dbCode: dbCode, tableName: tableName, fileName: fileName, type: referenceType, Page: page), success: { (result) in
print("3333333")
}, error: { (errorCode) in
print("3333333")
}) { (failureDes) in
print("3333333")
}
print("end 3333333")
}
let result = group.wait(timeout:.distantFuture)
switch result {
case .success:
print("不超时, 上面的两个任务都执行完")
case .timedOut:
print("超时了, 上面的任务还没执行完执行这了")
}
print("完成")
}
/*打印结果
mmmmmmmmmmmmmmmmm
begin 111111111111
end 111111111111
begin 2222222222
end 2222222222
begin 3333333
end 3333333
不超时, 上面的两个任务都执行完
完成
https://8888, bodyEncoding: Alamofire.JSONEncoding(options: __C.NSJSONWritingOptions(rawValue: 0)), urlParameters: [:])
11111
https://**, bodyEncoding: Alamofire.JSONEncoding(options: __C.NSJSONWritingOptions(rawValue: 0)), urlParameters: [:])
2222222222
https://**, bodyEncoding: Alamofire.JSONEncodinoptions: __C.NSJSONWritingOptions(rawValue: 0)), urlParameters: [:])
https://****, bodyEncoding: Alamofire.JSONEncoding(options: __C.NSJSONWritingOptions(rawValue: 0)), urlParameters: [:])
3333333
*/
栅栏函数(barrier)
等待异步执行多个任务后, 再执行下一个任务,一般使用barrier函数
//创建串行队列
// let queue = DispatchQueue.init(label: "test", qos: .default, attributes: .init(rawValue: 0), autoreleaseFrequency: .workItem, target: nil)
//创建并行队列
let queue = DispatchQueue.init(label: "test", qos: .default, attributes: .concurrent, autoreleaseFrequency: .workItem, target: nil)
queue.async {//任务一
for _ in 0...3 {
print("......")
}
}
queue.async {//任务二
for _ in 0...3 {
print("++++++");
}
}
queue.async(group: nil, qos: .default, flags: .barrier) {
print("group")
}
queue.async {
print("finish")
}
最后打印
......
++++++
++++++
++++++
++++++
......
......
......
group
finish
``
## 15 Block的三种类型 globalBlock、stackBlock 、mallocBlock 区别