转载于Github技术清谈
1.【问题】【iOS】请问前后台切换,会发生些什么,系统哪些方法会被调用,viewcontroller哪些方法会被调用
【答案】

前后台切换,主要的坑点在于:VC中并没函数会调用,尤其注意:VC 相关的 Appear 和 Disappear 函数并不会被调用。想在VC中监听切换,只能监听通知,每个在appdelegate的生命代理方法都有对应的通知。
如果考虑 APP 在后台被 kill 的情况:
进入后台后,如果没有后台运行权限及功能,可能在一段时间后被系统 kill 掉,再次进入app后,会重新进入启动流程。

参考链接:
标题&链接 | 手机端阅读 |
---|---|
标题:WWDC 2016 - Session 406-Optimizing App Startup Time
| |
链接:https://developer.apple.com/videos/play/wwdc2016/406/ | [图片上传失败...(image-9e8fdc-1584516454558)] |
2 【问题】【iOS】请问对无序的Array排序,有什么好的方法,代码越少,API越高级越好。有无原生方法可以办到。
苹果为我们提供了很多 Array 的排序方法,但原理上可以看到就是 Comparator (比较器) 和 Descriptor (描述器) 两种,像是 Selector 和 Function ,最终也是使用 Comparator 在做排序,只是响应方法不同。
其中 Swift 也有方法: array.sort(),见
参考链接:
标题&链接 | 手机端阅读 |
---|---|
标题:Apple Documentation-Swift-Array-sorted | |
链接:https://developer.apple.com/documentation/swift/array/2296815-sorted | [图片上传失败...(image-1c1d2-1584516454558)] |
先说说 Comparator ,如果数组中元素是 String 或 Number,首选 Comparator,可以将 compare: 方法的返回值直接作为 NSComparisonResult 返回值。实际的排序代码三行就可以搞定。当然用 Selector 和 Function,也是一样的效果,但需要写更多的代码。
例子一:
NSArray *sortedArray = [array sortedArrayUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2) {
if ([obj1 compare:obj2] == NSOrderedAscending) {
return NSOrderedAscending;
} else if ([obj1 compare:obj2] == NSOrderedDescending){
return NSOrderedDescending;
}else {
return NSOrderedSame;
}
}];
例子二:
- (void)arraySortUsingCompare {
// 比较器 排序
NSMutableArray *arr = [NSMutableArray array];
for (int i = 0; i < 10; i ++) {
int n = arc4random() % (10 - 0) + 1;
[arr addObject:@(n)];
}
NSLog(@"排序前 ===== %@", arr);
[arr sortUsingComparator:^NSComparisonResult(NSNumber *num1, NSNumber *num2) {
// return [num1 compare:num2]; // 正序
return [num2 compare:num1]; // 倒序
}];
NSLog(@"排序后 %@", arr);
arr = [NSMutableArray array];
[arr addObject:@"Kobe Bryant"];
[arr addObject:@"LeBorn James"];
[arr addObject:@"Steve Nash"];
[arr addObject:@"Stephen Curry"];
[arr addObject:@"Monkey D Luffy"];
[arr addObject:@"Roronoa Zoro"];
NSLog(@"排序前 ==== %@", arr);
[arr sortUsingComparator:^NSComparisonResult(NSString *str1, NSString *str2) {
// return [str1 compare:str2]; // 正序
return [str2 compare:str1]; // 倒序
}];
NSLog(@"排序后 %@", arr);
}
参考链接:
标题&链接 | 手机端阅读 |
---|---|
标题: 《Objective-C中的排序及Compare陷阱》 | |
链接:https://blog.csdn.net/u011883764/article/details/38868097 | [图片上传失败...(image-cd8752-1584516454558)] |
但是如果需要针对一个对象的几个属性作为不同的维度去做排序,那选择 Descriptor,因为不需要根据利用属性对排序优先级写一大堆的逻辑判断。主要将所有参与比较的属性都放入描述器中即可,如果想对球员的年龄和号码(优先级分先后)进行排序,只需要依次加入描述器组,三行代码就可以完成。
- (void)arraySortUsingDescriptor {
NSMutableArray *arr = [NSMutableArray array];
Person *person = [[Person alloc] init];
person.name = @"Ingram";
person.age = 21;
person.number = 14;
[arr addObject:person];
person = [[Person alloc] init];
person.name = @"Ball";
person.age = 21;
person.number = 2;
[arr addObject:person];
person = [[Person alloc] init];
person.name = @"Zubac";
person.age = 21;
person.number = 15;
[arr addObject:person];
NSSortDescriptor *ageDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:YES];
NSSortDescriptor *numberDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"number" ascending:YES];
[arr sortUsingDescriptors:@[numberDescriptor, ageDescriptor]];
for (Person *person in arr) {
NSLog(@"\n 球员姓名: %@ \n 球员号码: %d \n 球员年龄: %d \n -------- \n", person.name, person.number, person.age);
}
}
3【问题】【iOS】请问APNs推送如何区分设备,如何将设备的信息传给Apple,你上传的时机时怎样的,猜想这个设备信息是如何生成的
【答案】
【设备信息传递给apple】
post请求;
Use HTTP/2 and TLS 1.2 or later to establish a connection between your provider server and one of the following servers:
• Development server: api.sandbox.push.apple.com:443
• Production server: api.push.apple.com:443
也就是:
设备信息是通过一个 POST 请求将 DeveiceToken 和其他信息发送给 APNS,需要用 HTTP/2 和 TLS 1.2 或以上的版本,在自己提供的服务和以上服务之间建立连接。
- 开发环境: api.sandbox.push.apple.com:443
- 生产环境:api.push.apple.com:443
当然,还可以用一台机器的 2197 端口让 APNS 通过防火墙
请求示例:
HEADERS
- END_STREAM
+ END_HEADERS
:method = POST
:scheme = https
:path = /3/device/00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f0
host = api.sandbox.push.apple.com
authorization = bearer eyAia2lkIjogIjhZTDNHM1JSWDciIH0.eyAiaXNzIjogIkM4Nk5WOUpYM0QiLCAiaWF0I
jogIjE0NTkxNDM1ODA2NTAiIH0.MEYCIQDzqyahmH1rz1s-LFNkylXEa2lZ_aOCX4daxxTZkVEGzwIhALvkClnx5m5eAT6
Lxw7LZtEQcH6JENhJTMArwLf3sXwi
apns-id = eabeae54-14a8-11e5-b60b-1697f925ec7b
apns-expiration = 0
apns-priority = 10
apns-topic = com.example.MyApp
DATA
+ END_STREAM
{ "aps" : { "alert" : "Hello" } }
【上传时机】didRegisterForRemoteNotificationsWithDeviceToken
方法,回调内处理设备信息上传的业务。但有些情况是,我们希望根据用户账号来做推送,例如即时通讯应用。那么我们就要在登录或自动登录后,上传deviceToken,和用户信息绑定并处理替换逻辑,避免推送错乱。
【设备信息】This address takes the form of a device token unique to both the device and your app.
猜测:UDID+bundleId+生产/开发环境+时间戳。
其中注意带时间戳hash是为什么频繁上传device token的主要原因。长期不活跃app,比如用户一个月或者两个月没打开过该app,该服务器后端就再也推不到了。
4【问题】【果粉常识题】为什么iPhone有的后置摄像头上会有一个小孔,这可能是iPhone背面上唯一可能进水的部分,这个孔是干什么的
【答案】 iPhone 背部的小孔实际上是一个麦克风,它的作用是降噪。,故意放在远离嘴巴的位置,收集环境噪音,在通话时将环境噪音删掉。采用的是【双 Mic 降噪技术】
双脉降噪技术主要原理是,频率相同的声波会发生干涉,如果波频相同,振幅相反,则会互相抵消。
假设,手机底部的 Mic 收集到的声波是 Va,手机背部手机到的是 Vb;声波通过干涉相抵的简易公式是 Vm = (Va - Vb) * n; n 是差分电路的放大系数。
当手机处在一个噪音环境下,前后两个 Mic 收录的音波几乎相同,Vm1 = (Va1 - Vb1) * n; 若 Va1 - Vb1 无限趋近于 0,则 Vm1 无限趋近于 0
当我们利用 Mic 通话时,Vm2 = (Va2 - Vb2) * n, Va2 >> Vb2.
故而当我们及时身处在一个不算安静的环境,只要不遮挡手机背面的 Mic,我们听到的声音就是 Vm = Vm2 - Vm1, 理想情况下,Vm1 约等于 0; 但也有一些非理想的情况,例如,说话说未能对准 Mic,Va2 不够大,影响收音效果,声音变小。
AirPods 也是拥有两个 Mic,但 AirPods 采用的是上行降噪技术,筛选信噪,并做放大和降低。不过,耳机的降噪就是另外一个故事了。
5【问题】【计算机常识】小地做了一个微信机器人,每天给女神发今天天气真不错,你在干嘛?女神一直没回复。直到第99天,女神回复微信说,你这样做是没有用的。小地看到消息,高兴地说:我的机器人终于通过测试了!大风哥,拍着他的肩膀,语重心长地说:小地,你高兴得太早了。请回答:小地为什么高兴,大风哥为什么说他高兴得太早了。
【答案】小地高兴在于他以为他的机器人通过了图灵测试,大风哥,说他高兴太早,是因为图灵测试的完整定义是指测试者与被测试者(一个人和一台机器)隔开的情况下,通过一些装置(如键盘)向被测试者随意提问。 进行多次测试后,如果有超过30%的测试者不能确定出被测试者是人还是机器,那么这台机器就通过了测试,并被认为具有人类智能。
这里30%的测试者无法成立。
6【问题】【iOS】小地是一名base北京的iOS程序员,他有一次要出差深圳,但想找几个能在深圳可以一起吃早餐的朋友。但他的iPhone没有越狱,于是他在北京打开Xcode修改了系统定位到深圳,成功在探探、微信、陌陌上分别找到了可以共度早餐的人。但由于连续看了三天的妇联首映,加之几夜没睡也没吃早餐,他早晨在酒店无法下床,他想故技重施,打开公司打卡软件修改位置打卡。但发现无法修改。只好请假下午去,扣了半天工资。请问:小地他怎么改的位置,为什么公司打卡软件改不了,是怎么做的?
【答案】使用gpx文件, Xcode 虚拟定位可修改模拟器以及真机的系统的定位。即使是从App Store下载的探探、微信、陌陌只要使用系统接口获取定位信息,就可以通过gpx模拟定位。
1 Xcode 中新建一个项目, 创建一个 .gpx 文件
2 高德获取经纬度,链接 https://lbs.amap.com/console/show/picker , iOS 端采用的定位坐标系是 WGS-84 , 而获得的定位一般是火星坐标 GCJ-02,把 获得的坐标转化为原始坐标:WGS-84或者直接用 Google 地图获取坐标,就不需要坐标系转换了。还有一个 gpx 文件生成网站:https://www.gpxgenerator.com/ 可以直接选择坐标,然后导出 gpx 文件。
3 修改代码 把 Location.gpx 文件里的代码 改为 要定位的经纬度
4 点击 Debug--->Simulate Location --> Location 然后运行
(Location 这个是新创建 gpx 的文件名)
举例 Location.gpx :

钉钉也可以修改定位打卡,如果钉钉设置 Wi-Fi 打卡,公司只配置校验了 SSID,没有校验 DHCP 地址。把家里的 Wi-Fi 名称改得和公司打卡的 Wi-Fi 即可。
如果有一些打卡软件不能修改,那可能是因为未使用系统定位方法,或者未仅仅使用该方法作为定位依据。具体使用何种方法,可在另行讨论。但已知 iOS 定位方法有:GPS定位、基站蜂窝定位、Wi-Fi定位等多种定位方法,
精准度优先级可以为:
首选是 GPS,
然后是 GPRS(IP 和 routetrace ),蜂窝网络进行 routetrace 可以获取到第一个接入点
然后是 Wi-Fi(IP 和 routetrace 和附近的 Wi-Fi 名),SSID 名的优先级比较低,主要是靠routetrace
然后是附近的蓝牙设备
如果结合以上多种定位方法,这四个方案是同时的,组合起来命中率就很高了,取合理的定位结果交集进行反作弊也是一种思路。
但是在iOS13中对 Wi-Fi 定位权限进行了收紧,需要用户进行额外授权,相关的上下文可以参考下面的链接:
另外,做灰产的,有一种外设,基于苹果 MFi 认证,配合配套软件直接提供修改后的坐标,Apple 的这一类产品,最初是给那些没有 GPS 模块的iPad、iPod做定位的功能弥补,让这些用户也能使用定位。但这类产品修改的也是系统定位,进而也可以间接做打卡用图。不过是需要实体的装置。这个就不在讨论范围了。
还有其他几种相关的情况,见下图:

7【问题】【iOS】小地是一名中级iOS开发,他最近学会了iOS黑魔法 - Method Swizzling ,他感觉黑魔法很方便,于是他大量使用了这个技巧,并默默分享给其他同事,想大家一起成长,一起用。大风哥是团队一名高级开发,听到后,告诫小地谨慎使用。如果你是大风哥,你会怎么去告诫他。
答案】
优点:
区别于⼿动为每⼀个类编写埋点⽅法或者写⼀个基类来做统⼀的埋点,前两者在某些场景下⼯ 作量都不算⼩。可以做⼀个 UIViewController 的 Category,置换原⽣⽅法,在置换⽅法中将写⼊埋点代码,这样可以直接⼀键埋点完成。之后新增的 UIViewController 类也不需要再关⼼这些的埋点代码。
- (void)cyl_APOViewDidLoad {
Class class = [self class];
if (!([class isEqual:[UIViewController class]] || [class isEqual: [UINavigationController class]])) {
NSLog(@"统计该⻚⾯ %@", class);
}
}
置换 NSDictionary 的 -setObject:forKey: 方法,用于防止 crash。 NSArray 同理。
- (void)cyl_safeSetObject:(id)object forKey:(id<NSCopying>)key {
if (object && key) {
[self safe_setObject:object forKey:key];
}
}
Swift 版本实现:

缺点:
总结:一时hook一时爽,debug火葬场。
原因:
以下为 Stack Overflow-What are the Dangers of Method Swizzling in Objective C?
中列举出的7个问题:
Method swizzling is not atomic
Changes behavior of un-owned code
Possible naming conflicts
Swizzling changes the method's arguments
The order of swizzles matters
Difficult to understand (looks recursive)
Difficult to debug
可见,其没有类似注解的东⻄,⽅法置换没有有效声明。如果滥⽤,反⽽会增加维护成本。若擅⾃使⽤未同步其他同学,会成为极⼤的项⽬隐患。尤其是⼀些封装的模块。
这里着重说明几个场景:
场景:(iTeaTime(技术清谈)@国家一级保护废物 提供答案)
如果多次hook了同一个类的同一个方法,
跟分类重名的表现是一样:表现为无法控制执行的先后顺序,与编译器build的顺序有关,但编译器顺序有不可控性。
比如下面的实现方法,可能出现方法覆盖的问题:
+ (void)load {
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewDidLoad); SEL swizzledSelector = @selector(XK_ViewDidLoad);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
场景:(iTeaTime(技术清谈)@molon 提供)
Hook了具有继承关系的相同方法。
以下场景:
如果子类并没有重写父类的方法,拿父类的implement去swizzling本来就是错误的行为。
A<—继承---B<—继承---C
(B是A的子类,C又是B的子类)
A 里有 test 方法,但是 B 和 C 都没有重写。
通常如果要对 B 或者 C 的 test 进行hook的话,很多开发者都喜欢去给 B 或者 C add A.test 的 implemention。
那如果先hook的C,又hook的B,似乎就形成了C与A直接打交道的局面。但是以面向对象来说,C的原实现应该是B的当前实现才合理。
所以不应该hook当前类没有重写的方法,这种其实直接继承(或者加category方法)就可以做了,不需要hook,需要调用原实现直接[super test]即可。
8【问题】【iOS】iPhone在无耳机状态下,通过实体按键设置静音后,以下路径比如: 微信主tab-朋友圈-点开feed流中的小视频,可以播放声音。 通过点击头像-个人朋友圈主页,点开视频无法播放声音。即使按声音增加键也无法播放。请问这个表现不一致的现象,是feature还是bug,如果是bug你觉得是代码哪里写的有问题。写出修复代码。
【答案】 :
视频播放器默认静音模式下是没有声音的,但可以控制即使是静音模式下依然有声音,显然前者设置了,后者没有设置。推测前者是被提交了bug所以fix掉了,后者使用场景比较少,所以没有被注意到。
//忽略静音按钮
AVAudioSession *session =[AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
完整代码:
- (AVAudioPlayer *)player {
if (!_player) {
NSURL *URL = [[NSBundle mainBundle] URLForResource:@"xxxx.wav"
withExtension:nil];
_player = [[AVAudioPlayer alloc] initWithContentsOfURL:URL error:nil];
AVAudioSession *autioSession = [AVAudioSession sharedInstance];
[autioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
[autioSession setActive:YES error:nil];
[_player prepareToPlay];
}
耳机场景下,统一做了处理,都可以播放视频带声音。
比如以下代码用于判断耳机状态,因为AVAudioSession是单例,对耳机优先处理即可。
- (BOOL)isHeadsetPluggedIn {
AVAudioSessionRouteDescription* route = [[AVAudioSession sharedInstance] currentRoute];
for (AVAudioSessionPortDescription* desc in [route outputs]) {
if ([[desc portType] isEqualToString:AVAudioSessionPortHeadphones])
return YES;
}
return NO;
}
9 问题】【大前端】为什么Flutter会选择一门新语言dart而不是js等其他更普及的语言。
【答案】Flutter团队一次采访中透露因为做flutter的团队当时和dart的团队坐的很近,为了方便沟通。哈哈,团队协作是非常重要的因素。
当然,也不可忽视 Dart 语⾔的特性:

10 问题】【iOS-autolayout】一个ScrollView上有3个UILabel,每个label字数不固定,类似字数很多的那种,要求上下依次排列,当文字超出ScrollView的时候可以滑动,左右不能滚动,上下可滚动。
【答案】
三个label 那个,就是放了个scrollview 然后里面放三个label,从上往下边距全部约束为0,然后label 宽度与scrollview相同,最下面那个label距离底部scrollview为0。(在内部无需多放view)
1.在 Scrollview 添加⼀个 ContainView
2.ContentView 完全覆盖 Scrollview
3.ContainView 上添加了三个 Label。View 的 bottom 和 第三个 Label 的 bottom 做约束。
4.三个 Label 互相做间距和宽的约束,不约束⾼

5.通过将 Scrollview 的 ContentSize 和 ContainView 的 size 保持⼀致。
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.contentView layoutIfNeeded];
self.scrollview.contentSize =
CGSizeMake(CGRectGetWidth(self.contentView.frame), CGRectGetHeight(self.contentView.frame));
}
Demo 如下:
ScrollViewDemo.zip
11 【问题】【编程基础】如何用一行代码,互换两个变量的值,且不产生第三个变量
【方法一】【啥玩意儿啊-iOS-北京,提供答案】利用Swift元组特性:
可以在定义的同时就取出元祖中的值
// 相当于同时定义了三个变量
let (name, age, score) = (“a”, 30, 99.9)
根据这一特性,我们可以这样互换值:
(a, b) = (b, a)
【方法二】【消摇提供答案】
(a = a ^ b) && (b = a ^ b) && (a = a ^ b) 或者这样 a = a ^ b; b = a ^ b; a = a ^ b;
(a = a + b) && (b = a - b) && (a = a - b)
(a = a x b) && (b = a / b) && (a = a / b)
【方法三】【消摇提供答案】
a = ( a + b ) - b * ( b = a );
12 问题】【iOS】如何给view同时加上圆角和阴影?至少给出两种实现方法,使用到的API越高级越好。
【提示】两种方法,答案提示:UIBezierPath,和iOS11 layer有个新的方法
【答案】iOS11的layer是maskedCorners,CACornerMask。
标题:ios 圆角 cornerRadius 对性能的影响究竟多大? 你测试过吗?
链接:https://www.jianshu.com/p/13820dbf5d20
13 问题】【计算机常识】在开发中,我们在重连等场景中,为避免造成过度的资源消耗,我们常常把重试的时间间隔做递增处理,有时是指数级增长方式,比如第1次与第二次时间间隔为2秒,第二次与第三次时间间隔为4秒,然后是8秒,我们有时也按照1,3,5这样的规律递增。这种编程技巧的名称是什么?
【答案】指数退避算法。
14 【问题】【算法】《易传·系辞上传》:“易有太极,是生两仪,两仪生四象,四象生八卦。”,这句话结合下面的几张图,与计算机数据结构中的哪个概念更为相近,描述该概念,越详细越好。问题如下:
六十四卦所在结构中节点数是多少?
六十四卦中的任意一卦,在上述数据结构体系中对应的概念叫什么?
六十四卦中的任意一卦的高度与深度分别是多少?
“完全” 与 “满” 用哪一个,形容下面的图片更为贴切?


【答案】总体是二叉树,高度是 0 深度是7,节点数是 2ˆ7-1, 满二叉树。参考下图:

15 【问题】【hybrid-app】js与native交互中,js如何调用native方法,native如何调js方法,借助的中间foundation叫什么。给出核心步骤对应函数,重点给出前端和native需要约定联调的部分。
【提示】推荐 《一本走心的 JS-Native 交互电子书》 。
【答案】【群成员闪舞提供】
native 能直接调用 js
[webView stringByEvaluatingJavaScriptFromString:javascriptCommand];
js 不能直接调用 native 的方法,但是可以间接的通过一些方法来实现。可以利用 UIWebView 的 webView: shouldStartLoadWithRequest: navigationType: 代理方法来做。 WKWebView 中可以通过 webView: decidePolicyForNavigationAction: decisionHandler:
要想触发native 的 webView: shouldStartLoadWithRequest: navigationType方法,可以通过下面两种形式:
1.创建 iframe 标签
2.设置 window 的 location
核心步骤:
1. native 加载 html,其中的 script 标签里(也就是 js 代码), js 创建
iframe 标签,并设置它的 src 属性为
wwvjbscheme://**BRIDGE_LOADED**,并且把回掉放到一个数组里
2.在 webView: shouldStartLoadWithRequest: navigationType 方法中拦截
wvjbscheme://**BRIDGE_LOADED**,并加载本地(桥中)的 js
3.本地(桥) js 创建隐藏的 iframe 标签并且修改 src 为
wvjbscheme://**WVJB_QUEUE_MESSAGE**,这样就又能在 webView:
shouldStartLoadWithRequest: navigationType 拦截了。
16 【问题】【iOS】猜想dequeueReusableCellWithIdentifier的实现是怎样的,给出示例代码。注意边界条件:相邻cell的identifier相等时。你的实现中该函数的时间复杂度是多少。为什么?
cell复用机制的实现猜想,见 GitHub-Chameleon :
- (UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier
{
for (UITableViewCell *cell in _reusableCells) {
if ([cell.reuseIdentifier isEqualToString:identifier]) {
UITableViewCell *strongCell = cell;
// the above strongCell reference seems totally unnecessary, but without it ARC apparently
// ends up releasing the cell when it's removed on this line even though we're referencing it
// later in this method by way of the cell variable. I do not like this.
[_reusableCells removeObject:cell];
[strongCell prepareForReuse];
return strongCell;
}
}
return nil;
}
时间复杂度为: O(n)
注意:
NSArray
/ NSMutableArray
containsObject:
,containsObject:
,indexOfObject*
,removeObject:
会遍历里面元素查看是否与之匹对,所以复杂度等于或大于 O(n)。
这里 _reusableCells
使用的是NSMutableSet
,而
NSSet
/ NSMutableSet
/ NSCountedSet
这些集合类型是无序没有重复元素。这样就可以通过 hash table 进行快速的操作。比如 addObject:, removeObject:, containsObject: 都是按照 O(1) 来的。需要注意的是将数组转成 Set 时会将重复元素合成一个,同时失去排序。
加之 for 循环,可以得到复杂度计算结果。
17 【问题】如图:用autolayout做下横竖屏适配:里面:蓝色是父视图,子视图是Label和图片,图片可拉伸。Label和图片总是相对居中显示。对图片顶端,label底端,拉伸,水平总是居中。

【答案】把两个子视图,包一层View,不一定是StackVIew,也可以是普通View,内部搞好约束,top、left、bottom、right。再对中间层view做水平、垂直居中操作即可。
类似前端方案:【十代-小公司-泉州iOS提供】
使用 FlexLib 框架:
网友评论