ag真人是时候干掉,实现同工程多项目配置

2019-09-14 16:34栏目:专项工作
TAG:

由于本期产品需求中有一个保存人员信息到通讯录的功能,因为这部分 iOS 8 使用到<AddressBook/AddressBook.h>库已经不维护了,iOS9+ 需要<Contacts/Contacts.h>库,因此如果需要继续适配 iOS8 的话,就要写对应的两套代码,查看了数据统计之后发现我们目前没有 iOS8 的用户,就不想感动自己继续适配 iOS8,因此总结了一下信息向leader申请干掉 iOS8

ag真人 1

一、开始建target

1. 苹果官方于今年6月份的 WWDC 上推出了 iOS11 系统,当时统计的各系统的数据占比如下:
  • iOS10 系统占比 89%
  • iOS9 系统占比 9%
  • 其他系统占比 2%

目前 iOS8 系统最多占比 2%,而且针对我们产品的目标人群:办公人士的低版本系统占比会更低

数据来源:

最新的只显示 iOS11和 iOS10了

ag真人 2

前言

1.点击duplicate,会出现个ReaderCopy -target

2. 砍掉 iOS8 的好处

可以直接使用最低版本为 iOS9 的api,不需要一些额外的适配工作,提高项目稳定性,代码也会更加简洁

  • 比如有些场景使用 UIStackView,不需要进行额外的布局适配
  • 增强的UIKit动力学,如用于非矩形碰撞边界检测等
  • 新增NSDataAsset类,使得更加方便获取定制设备的内存和图形处理能力
  • 所有标准的UIKit控件翻转,能够支持从右到左的语言。此外,导航,手势,Collection View和TableViewCell的布局也适当地翻转
  • MapKit框架对功能进行了一些补充,比如支持查询公交的预计到达时间和公交路线、地图视图支持了3D天桥模式、注释可以完全定制
  • PassKit框架在Apple Pay支持Discover cards、借记卡和信用卡。
  • SFSafariViewController可以用来显示你的应用程序中的网页内容。它和safari共享Cookie和其他网站数据,并有许多的Safari浏览器的强大功能,如Safari自动填充和Safari阅读器。与Safari浏览器本身相比,SFSafariViewController UI是一个单一的Web页面,有一个完成按钮可以使用户回到应用程序中。
  • 如果您的应用程序显示的网页内容,但不自定义内容,可以考虑更换WKWebView或UIWebView为SFSafariViewController
  • HealthKitFramework对跟踪生殖健康和紫外线照射的等新领域的支持。
  • 使用AVPictureInPictureControllerAVPlayerViewController类,实现Picture in Picture功能。
  • FoundationFramework 中 Strings文件的上下文相关的可变宽度的字符串支持。
  • 对于 iPad,可以更方便地使用分屏多任务特性,如画中画视频播放等
  • 另外一些扩展也新增了一些特性更多详细 iOS9 新特性点击这里-官网说明

Blocks是C语言的扩充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了这个新功能“Blocks”。从那开始,Block就出现在iOS和Mac系统各个API中,并被大家广泛使用。一句话来形容Blocks,带有自动变量的匿名函数。

ag真人 3

2. 可以在这里贴一下当前产品的统计数据,月活的 iOS8 用户占比,肯定很少

Block在OC中的实现如下:

2、修改名字

3. 以下为不同型号手机支持的最高系统版本:
  • iPhone 4 最高支持7.1.2
  • iPhone 4s 最高支持9.3.5
  • iPhone 5 最高支持10.3.3
  • iPhone 5c 最高支持10.3.3
  • iPhone 5s及以上都支持到 11.0

structBlock_layout {void*isa;intflags;intreserved;void(void*, ...);structBlock_descriptor *descriptor;/* Imported variables. */};structBlock_descriptor {unsignedlongintreserved;unsignedlongintsize;void(void*dst,void*src);void;};

1)慢速双击ReaderCopy ,修改成你想要的名字

4. 以下为不同型号手机支持的最低系统版本:
  • iPhone 5 最低支持 6.0
  • iPhone 5c 最低支持 7.0
  • iPhone 5s 最低支持 7.0
  • iPhone 6 最低支持 8.0
  • iPhone 6s 最低支持 9.0
  • iPhone SE 最低支持 9.3
  • iPhone 7 最低支持 10.0
  • iPhone 8 及以上最低支持 11.0

ag真人 4

ag真人 5

5. 主要竞品的最低适配版本:
  • 钉钉 7.0 (钉钉的部门目标用户是餐饮等传统行业,用户 iPhone 系统版本应该会略低于普通互联网公司)
  • Slack 9.0 (Slack 创始团队就是一帮程序员,主要目标用户也是互联网小团队,适配版本高于其他一般 App 也是理所应当)
  • BearyChat 9.0(BearyChat的模式大部分与 Slack 类似,相当于国内的 Slack)
  • 企业微信 8.0
  • 美团大象 8.0
  • 纷享销客 8

按照以往的经验,一般的App 都是保持适配三个最新的系统版本(比如微信、淘宝、美团等是在去年年末抛弃了iOS7.0,注:去年推出了iOS10)

砍掉之后,iOS8 新老用户在appStore 只能搜索到支持 iOS8的最高版本,

从结构图中很容易看到isa,所以OC处理Block是按照对象来处理的。在iOS中,isa常见的就是_NSConcreteStackBlock,_NSConcreteMallocBlock,_NSConcreteGlobalBlock这3种(另外只在GC环境下还有3种使用的_NSConcreteFinalizingBlock,_NSConcreteAutoBlock,_NSConcreteWeakBlockVariable,本文暂不谈论这3种,有兴趣的看看官方文档)

2)修改新生成的infoplist名字,和上面一样,双击修改名称就ok了,

6. 注意事项
  • 检查一下提醒升级及强制升级的相关参数,比如下版本不支持 iOS8 用户了,那么对于 iOS8 用户的升级提醒要屏蔽掉,并提前和 BS 同学确认相关问题
  • 对于可预见的接口需求上的改动、提前做好老版本的适配工作,即使某些功能不能正常使用也要做相应的屏蔽或者 PlaceHolder
  • 干掉 一个老版本 之后,可以查看下对应的警告,把该版本专门的适配代码也相应干掉;有些被弃用的api或枚举值也会报警告,最好进行相应更新

以上介绍是Block的简要实现,接下来我们来仔细研究一下Block的捕获外部变量的特性以及__block的实现原理。

然后到你新建的target中,画圈的部分会显示让你选择infoplist,选择刚才你改名字的infoplist文件即可,选完切换一下就出来了

7. 去年今日:得知是最后一版适配 iOS8 的时候,高兴的像个三百斤的胖子

ag真人 6iOS7 适配比 iOS8 逆天多了

参考:iOS9新特性-官方说明iOS系统-官网统计

研究工具:clang

ag真人 7

为了研究编译器的实现原理,我们需要使用 clang 命令。clang 命令可以将 Objetive-C 的源码改写成 C / C++ 语言的,借此可以研究 block 中各个特性的源码实现方式。该命令是

3)点击edit Scheme ,再点击Manager Schemes

clang -rewrite-objc block.c

ag真人 8ag真人 9ag真人 10

目录

4)选到*copy的那个,慢速双击,改成你想改的名字

1.Block捕获外部变量实质

3、配置宏定义

2.Block的copy和release

1.在项目的Build Settings里配置Swift Compiler - Custom Flags,展开Other Swift Flags,在Debug右侧输入“-DDMW”。

3.Block中__block实现原理

swift:

一.Block捕获外部变量实质

#ifDMW

ag真人 11

#elseif 'oldname'

拿起我们的Block一起来捕捉外部变量吧。

#else

说到外部变量,我们要先说一下C语言中变量有哪几种。一般可以分为一下5种:

#endif

自动变量

OC:

函数参数

#if (defined DMW)

静态变量

#elseif (defined 'oldname')

静态全局变量

#else

全局变量

#endif

研究Block的捕获外部变量就要除去函数参数这一项,下面一一根据这4种变量类型的捕获情况进行分析。

4、新的target就创建完了,根据上面的#if #else.... 区分项目进行配置

我们先根据这4种类型

自动变量

静态变量

静态全局变量

全局变量

写出Block测试代码。

ag真人 12

这里很快就出现了一个错误,提示说自动变量没有加__block,由于__block有点复杂,我们先实验静态变量,静态全局变量,全局变量这3类。测试代码如下:

#importintglobal_i =1;staticintstatic_global_j =2;intmain(intargc,constchar* argv[]) {staticintstatic_k =3;intval =4;void = ^{ global_i ++; static_global_j ++; static_k ++;NSLog(@"Block中 global_i = %d,static_global_j = %d,static_k = %d,val = %d",global_i,static_global_j,static_k,val); }; global_i ++; static_global_j ++; static_k ++; val ++;NSLog(@"Block外 global_i = %d,static_global_j = %d,static_k = %d,val = %d",global_i,static_global_j,static_k,val); myBlock();return0;}

运行结果

Block 外 global_i =2,static_global_j =3,static_k =4,val =5Block 中 global_i =3,static_global_j =4,static_k =5,val =4

这里就有2点需要弄清楚了

1.为什么在Block里面不加__bolck不允许更改变量?

2.为什么自动变量的值没有增加,而其他几个变量的值是增加的?自动变量是什么状态下被block捕获进去的?

为了弄清楚这2点,我们用clang转换一下源码出来分析分析。

(main.m代码行37行,文件大小832bype, 经过clang转换成main.cpp以后,代码行数飙升至104810行,文件大小也变成了3.1MB)

源码如下

intglobal_i =1;staticintstatic_global_j =2;struct__main_block_impl_0 {struct__block_impl impl;struct__main_block_desc_0* Desc;int*static_k;intval; __main_block_impl_0(void*fp,struct__main_block_desc_0 *desc,int*_static_k,int_val,intflags=0) : static_k(_static_k), val { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};staticvoid__main_block_func_0(struct__main_block_impl_0 *__cself) {int*static_k = __cself->static_k;// bound by copyintval = __cself->val;// bound by copyglobal_i ++; static_global_j ++; (*static_k) ++;NSLog((NSString*)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_6fe658_mi_0,global_i,static_global_j,(*static_k),val); }staticstruct__main_block_desc_0 { size_t reserved; size_t Block_size;} __main_block_desc_0_DATA = {0,sizeof(struct__main_block_impl_0)};intmain(intargc,constchar* argv[]) {staticintstatic_k =3;intval =4;void = &__main_block_impl_0__main_block_func_0, &__main_block_desc_0_DATA, &static_k, val)); global_i ++; static_global_j ++; static_k ++; val ++;NSLog((NSString*)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_6fe658_mi_1,global_i,static_global_j,static_k,val); (__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);return0;}

首先全局变量global_i和静态全局变量static_global_j的值增加,以及它们被Block捕获进去,这一点很好理解,因为是全局的,作用域很广,所以Block捕获了它们进去之后,在Block里面进行++操作,Block结束之后,它们的值依旧可以得以保存下来。

接下来仔细看看自动变量和静态变量的问题。

在__main_block_impl_0中,可以看到静态变量static_k和自动变量val,被Block从外面捕获进来,成为__main_block_impl_0这个结构体的成员变量了。

接着看构造函数,

__main_block_impl_0(void*fp,struct__main_block_desc_0 *desc,int*_static_k,int_val,intflags=0) : static_k(_static_k), val

这个构造函数中,自动变量和静态变量被捕获为成员变量追加到了构造函数中。

main里面的myBlock闭包中的__main_block_impl_0结构体,初始化如下

void = &__main_block_impl_0__main_block_func_0, &__main_block_desc_0_DATA, &static_k, val));

impl.isa = &_NSConcreteStackBlock;

impl.Flags = 0;

impl.FuncPtr = __main_block_impl_0;

Desc = &__main_block_desc_0_DATA;

*_static_k = 4;

val = 4;

到此,__main_block_impl_0结构体就是这样把自动变量捕获进来的。也就是说,在执行Block语法的时候,Block语法表达式所使用的自动变量的值是被保存进了Block的结构体实例中,也就是Block自身中。

这里值得说明的一点是,如果Block外面还有很多自动变量,静态变量,等等,这些变量在Block里面并不会被使用到。那么这些变量并不会被Block捕获进来,也就是说并不会在构造函数里面传入它们的值。

Block捕获外部变量仅仅只捕获Block闭包里面会用到的值,其他用不到的值,它并不会去捕获。

再研究一下源码,我们注意到__main_block_func_0这个函数的实现

staticvoid__main_block_func_0(struct__main_block_impl_0 *__cself) {int*static_k = __cself->static_k;// bound by copyintval = __cself->val;// bound by copyglobal_i ++; static_global_j ++; (*static_k) ++;NSLog((NSString*)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_6fe658_mi_0,global_i,static_global_j,(*static_k),val); }

我们可以发现,系统自动给我们加上的注释,bound by copy,自动变量val虽然被捕获进来了,但是是用 __cself->val来访问的。Block仅仅捕获了val的值,并没有捕获val的内存地址。所以在__main_block_func_0这个函数中即使我们重写这个自动变量val的值,依旧没法去改变Block外面自动变量val的值。

OC可能是基于这一点,在编译的层面就防止开发者可能犯的错误,因为自动变量没法在Block中改变外部变量的值,所以编译过程中就报编译错误。错误就是最开始的那张截图。

Variable is not assignable(missing __block type specifier)

小结一下:

到此为止,上面提出的第二个问题就解开答案了。自动变量是以值传递方式传递到Block的构造函数里面去的。Block只捕获Block中会用到的变量。由于只捕获了自动变量的值,并非内存地址,所以Block内部不能改变自动变量的值。Block捕获的外部变量可以改变值的是静态变量,静态全局变量,全局变量。上面例子也都证明过了。

剩下问题一我们还没有解决。

回到上面的例子上面来,4种变量里面只有静态变量,静态全局变量,全局变量这3种是可以在Block里面被改变值的。仔细观看源码,我们能看出这3个变量可以改变值的原因。

静态全局变量,全局变量由于作用域的原因,于是可以直接在Block里面被改变。他们也都存储在全局区。

ag真人 13

静态变量传递给Block是内存地址值,所以能在Block里面直接改变值。

根据官方文档我们可以了解到,苹果要求我们在自动变量前加入__block关键字(__block storage-class-specifier存储域类说明符),就可以在Block里面改变外部自动变量的值了。

总结一下在Block中改变变量值有2种方式,一是传递内存地址指针到Block中,二是改变存储区方式。

先来实验一下第一种方式,传递内存地址到Block中,改变变量的值。

#importintmain(intargc,constchar* argv[]) {NSMutableString* str = [[NSMutableStringalloc]initWithString:@"Hello,"];void = ^{ [str appendString:@"World!"];NSLog(@"Block中 str = %@",str); };NSLog(@"Block外 str = %@",str); myBlock();return0;}

控制台输出:

Block 外 str = Hello,

Block 中 str = Hello,World!

看结果是成功改变了变量的值了,转换一下源码。

struct__main_block_impl_0 {struct__block_impl impl;struct__main_block_desc_0* Desc;NSMutableString*str; __main_block_impl_0(void*fp,struct__main_block_desc_0 *desc,NSMutableString*_str,intflags=0) : str { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};staticvoid__main_block_func_0(struct__main_block_impl_0 *__cself) {NSMutableString*str = __cself->str;// bound by copy(id, SEL,NSString*))objc_msgSend)str, sel_registerName("appendString:"), (NSString*)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_33ff12_mi_1);NSLog((NSString*)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_33ff12_mi_2,str); }staticvoid__main_block_copy_0(struct__main_block_impl_0*dst,struct__main_block_impl_0*src) {_Block_object_assign&dst->str, src->str,3/*BLOCK_FIELD_IS_OBJECT*/);}staticvoid__main_block_dispose_0(struct__main_block_impl_0*src) {_Block_object_disposesrc->str,3/*BLOCK_FIELD_IS_OBJECT*/);}staticstruct__main_block_desc_0 { size_t reserved; size_t Block_size;void(struct__main_block_impl_0*,struct__main_block_impl_0*);void(struct__main_block_impl_0*);} __main_block_desc_0_DATA = {0,sizeof(struct__main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};intmain(intargc,constchar* argv[]) {NSMutableString* str = ((NSMutableString*(id, SEL,NSString*))objc_msgSend)((NSMutableString*objc_msgSend)objc_getClass("NSMutableString"), sel_registerName, sel_registerName("initWithString:"), (NSString*)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_33ff12_mi_0);void = &__main_block_impl_0__main_block_func_0, &__main_block_desc_0_DATA, str,570425344));NSLog((NSString*)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_33ff12_mi_3,str); (__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);return0;}

在__main_block_func_0里面可以看到传递的是指针。所以成功改变了变量的值。

至于源码里面的copy和dispose下一节会讲到。

版权声明:本文由ag真人发布于专项工作,转载请注明出处:ag真人是时候干掉,实现同工程多项目配置