参考答案:
iOS Socket编程-Objective-C原生API版
iOS Socket编程-Objective-C基于CocoaAsyncSocket版
iOS Socket编程-Swift原生API版
iOS Socket编程-Swift基于AsyncSocket版
参考答案:
关于图片的压缩处理,在ios中常用的方法是先处理像素再处理尺寸。
参考答案:
最直接的办法就是使用UIImageView的contentMode,让其自动适应,但是这样会消耗一定的性能。
如果要优化,可以通过手动将原图处理成UIImageView大小的图,再给它呈现。
参考答案:
通常耗时的操作都会放在子线程里处理,然后再回到主线程来显示。下面举几个例子:
参考答案:
就想到这些了,大家再补充吧~
参考答案:
版本迭代一定要注意兼容老版本,比如新增了字段或者去掉了某些不再使用的字段,不能引起应用闪退。我们这里只谈程序代码兼容新老版本问题,不考虑业务。因为业务是要求后台来兼容的,通常接口会有版本号控制,用于兼容不同版本的客户端。
对于任何一个App,当可以升级的时候,不会是所有用户就立刻去升级,通常会有很大一部分的用户是不愿意立刻升级的。原因会有很多种,比如我这种的就不会频繁升级,因为对于我来说,这个App并不是天天用,没有必要升级。
那么,我们在iOS开发时,如何去兼容老版本的,保证新版本的增加或者删减不会影响到老版本呢?其实这个问题似乎并不是说有没有新、老版本问题,更重要的是程序的健壮性问题。
对于我们做前端的,永远不要相信后台一定会按照原先约定返回我们想要的数据结构以及所有字段。
假设接口返回来的数据是这样的,我们需要通过类型判断,确保不会因为接口变化返回无效数据而引起闪退。当然,当接口返回的数据结构与我们原先约定的不一样时,通常是因为后台出错了,因此为了程序更健壮,我们应该要容得下后台的错误:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
AFHTTPRequestOperation
*op
=
[
self
PostRequestWithUrl
:url
params
:params
completion
:
^
(
id
responseObject
)
{
BOOL
isSuccess
=
NO
;
if
(
[
responseObject
isKindOfClass
:
[
NSDictionary
class
]
]
)
{
NSDictionary
*response
=
responseObject
[
@"response"
]
;
if
(
[
response
isKindOfClass
:
[
NSDictionary
class
]
]
)
{
NSArray
*resultList
=
response
[
@"resultList"
]
;
if
(
[
resultList
isKindOfClass
:
[
NSArray
class
]
]
)
{
NSArray
*listModels
=
[
HYBCosmesisModel
objectArrayWithKeyValuesArray
:resultList
]
;
isSuccess
=
YES
;
completion
(
listModels
)
;
}
}
}
if
(
!
isSuccess
)
{
// 表示出错
completion
(
nil
)
;
}
}
errorBlock
:
^
(
NSError
*error
)
{
errorBlock
(
error
)
;
}
]
;
|
而我们在使用的时候,对于字符串、数组、字典都应该要做一下类型判断和空处理。比如:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
if
(
[
response
isKindOfClass
:
[
NSDictionary
class
]
]
)
{
NSString
*value
=
response
[
@"blogName"
]
;
if
(
!
kIsEmptyString
(
value
)
)
{
// Do my job
}
NSArray
*array
=
response
[
@"array"
]
;
if
(
[
array
isKindOfClass
:
[
NSArray
class
]
]
)
{
// Do my job
}
}
|
对于业务方面的话,由后台来做版本控制,通过在接口做添加公共参数,[email protected]�要调用的是哪个版本的接口。
参考答案:
这个问题非常简单,但是对于新手就不太清楚了。在很多小公司里,前端好像什么都不需要管,只等后台给你一个接口及参数说明就可以了,根本不清楚后端为什么要这么设计这个接口。
那笔者也来聊聊如何与后端服务器交互:
好了,就扯谈这些吧~
参考答案:
使用Dispatch Group追加block到Global Group Queue,这些block如果全部执行完毕,就会执行Main Dispatch Queue中的结束处理的block。
当放到group中的所有请求都完成时,才会回调dispatchgroupnotify的block:
1
2
3
4
5
6
7
8
9
10
|
dispatch_queue_t
queue
=
dispatch_get_global_queue
(
DISPATCH_QUEUE_PRIORITY_DEFAULT
,
0
)
;
dispatch_group_t
group
=
dispatch_group_create
(
)
;
dispatch_group_async
(
group
,
queue
,
^
{
/*加载图片1 */
}
)
;
dispatch_group_async
(
group
,
queue
,
^
{
/*加载图片2 */
}
)
;
dispatch_group_async
(
group
,
queue
,
^
{
/*加载图片3 */
}
)
;
dispatch_group_notify
(
group
,
dispatch_get_main_queue
(
)
,
^
{
// 合并图片
}
)
;
|
参考答案:
这个不太清楚想问什么,笔者翻看了看GCD中dispatchgroupt里,也没有什么可以设置同一个组内的任务依赖关系的,就看到dispatchgroupwait这个API:
1
2
3
4
|
long
dispatch_group_wait
(
dispatch_group_t
group
,
dispatch_time_t
timeout
)
;
|
这个API是等待group中的所有任务都执行完毕才能继续往下执行其它任务。它是同步地等待任务执行完毕。比如,A、B、C、D四个任务,要求A、B执行完毕后,C、D才能开始执行,那么可以通过这样做:
1
2
3
4
5
6
7
8
9
10
11
|
dispatch_queue_t
queue
=
dispatch_get_global_queue
(
DISPATCH_QUEUE_PRIORITY_DEFAULT
,
0
)
;
dispatch_group_t
group
=
dispatch_group_create
(
)
;
dispatch_group_async
(
group
,
queue
,
^
{
/* 任务A */
}
)
;
dispatch_group_async
(
group
,
queue
,
^
{
/* 任务B */
}
)
;
dispatch_group_notify
(
group
,
dispatch_get_main_queue
(
)
,
^
{
dispatch_group_async
(
group
,
queue
,
^
{
/* 任务C */
}
)
;
dispatch_group_async
(
group
,
queue
,
^
{
/* 任务D */
}
)
;
}
)
;
|
或者可以这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
dispatch_queue_t
queue
=
dispatch_get_global_queue
(
DISPATCH_QUEUE_PRIORITY_DEFAULT
,
0
)
;
dispatch_group_t
group
=
dispatch_group_create
(
)
;
dispatch_group_async
(
group
,
queue
,
^
{
/* 任务A */
}
)
;
dispatch_group_async
(
group
,
queue
,
^
{
/* 任务B */
}
)
;
// 同步等待A、B执行
dispatch_group_wait
(
group
,
DISPATCH_TIME_FOREVER
)
;
dispatch_release
(
group
)
;
// 重新创建组
group
=
dispatch_group_create
(
)
;
dispatch_group_async
(
group
,
queue
,
^
{
/* 任务C */
}
)
;
dispatch_group_async
(
group
,
queue
,
^
{
/* 任务D */
}
)
;
dispatch_group_notify
(
group
,
dispatch_get_main_queue
(
)
,
^
{
// C、D执行完毕后,想干嘛就干嘛去吧
}
)
;
|
不知道笔者对题目的理解是否到位,上面的代码是随手写的,可能单词会写错~~~
参考答案:
这个问题,笔者在另外一篇文章中写到,然后也在微博上收集了很多朋友们的想法。请大家稳步去阅读吧!
A. 可随机访问任何一个元素
B. 插入,删除操作不需要移动元素
C. 无需事先估计存储空间大小
D. 所欲存储空间可以是不连续的
这道题是考大学时所学的链表知识,其实笔者也忘了很多了。因为在大学时,曾经自己写过很多链表相关的代码,再加上经常帮同学调试,以及帮助同学讲解链表相关知识点,因此记忆较深。以下答案纯属个人认知,非百度而来,若有不对之处,请指出。
参考答案: (A)
链表不同于数组。链表之所有叫链表,就是像一条链一样,要过到某个节点处,就得遍历着找;而数组才具备随机访问任何一个元素的能力,数组可以通过索引直接访问元素,时间复杂度为常量,效率非常高,因此在某些场合上,我们需要数组这样的数据结构。
B. 链表的插入、删除都不需要移动元素,只需要修改指针的指向就可以了,因为链表上的每个节点都是动态分配的,分配在堆上,通过指针来指向每个节点的内存区,要获取某个节点的值,是需要遍历一遍才能找到对应的节点的。
C. 因为链表上的每个节点是分配在堆上,需要开发人员手动申请内存空间的,因此不像数组在定义时就要指定存储空间大小。对于链表,需要增加一个节点时,直接在堆上申请。当需要删除某个节点时,可以直接将该节点的内存给释放掉。
D. 因为链接中的节点都是存储在堆上的,而每个节点之间都有一个指向前一个节点和后一个节点的指针,只要知道链表头指针,就可以通过遍历查找到任何一个节点。因此,链表不同于数组,数组是要连续的内存存储空间,才能保证以常量时间复杂度快速访问任意元素;而链表不要求每个节点是连接,在堆上申请的内存空间很难得到连续的,而且空间产生内存碎片。
A. 多进程里,子进程可获取父进程的所有堆和栈的数据;而线程会与同进程的其他线程共享数据,拥有自己的栈空间。
B. 线程因为有自己的独立栈空间且共享数据,所有执行的开销相对较大,同时不利于资源管理和保护。
C. 线程的通信速度更快,切换更快,因为他们在同一地址空间内。
D. 线程使用公共变量/内存时需要使用同步机制,因为他们在同一地址空间内。
参考答案:
这道题要求不创建新的实例,只有a、b两个变量,要交换这两个变量的值,通常的做法是使用临时变量来临时存储,但是现在要求不使用新的实例,那么有什么办法呢?
方法就是通过位运算来操作:
1
2
3
4
5
|
a
=
a
^
b
;
b
=
a
^
b
;
a
=
a
^
b
;
|
对于题目中的a = 19,也就是对应二进制00010011;而b=29,也就是对应二进制00011101
注意,符号表示按位异或。所谓按位异或是指对应位置上的二进制数值相同为0,不同为1。
参考答案:
笔者之前写过这篇文章讲了讲开发中常见的内存循环引用的案例:
参考答案:
笔者也不是很确定,iOS里的序列化是指归档、JSON序列化吗?实际上归档也就是将对象转换成XML、JSON序列化也就是将对象转换data。
将对象JSON序列化:
1
2
3
4
5
6
7
8
|
NSLog
(
@"%s"
,
__FUNCTION__
)
;
NSDictionary
*dict
=
@
{
@"key"
:
@"value"
,
@"key1"
:
@"value1"
,
@"key2"
:
@"value2"
}
;
NSData
*data
=
[
NSJSONSerialization
dataWithJSONObject
:dict
options
:NSJSONWritingPrettyPrinted
error
:nil
]
;
NSLog
(
@"%@"
,
[
[
NSString
alloc
]
initWithData
:data
encoding
:NSUTF8StringEncoding
]
)
;
|
将对象归档:需要遵守NSCoding协议,实现如下方法:
1
2
3
4
5
|
-
(
void
)
encodeWithCoder
:
(
NSCoder
*
)
aCoder
{
[
aCoder
encodeObject
:self
.
title
forKey
:
@"title"
]
;
}
|
如果所谓的序列化不是指这两种,还请高人指点。
参考答案:
不知道说得合不合适,还请高人提出还有哪些方式?
参考答案:
想要更深入,不防看看大牛的文章吧:iOS 保持界面流畅的技巧
参考答案:
设计API的基本准则是:
注意事项:
分析:
要想让全工程使用起来非常方便,那最好的方式就是使用单例,比如SVProgressHUD就是通过单例的方式来操作的。将单例封闭在内部,外部并不知道是单例,而外部的调用全是通过类方法的形式来调用,代码调用是非常简化的。使用单例的优点是方便管理和调用。缺点就是一直占用内存而不释放。
当然,我们也可以通过正常的对象创建,在哪里使用就在哪里创建一个对象,自己来管理。这样的方式在使用的地方不是那么方便,还需要再单独进行一层封装,以方便直接调用。但这种方式的好处就是在不需要使用的时候可以释放掉;缺点就是如果同时创建了多个HUD来显示时,需要调用者使用代码逻辑来控制之前的显示与隐藏或者切换文本等。
笔者觉得,使用单例方式更方便调用一些,外部也不用通过逻辑来管理多个HUD的显示与隐藏问题,都封装到内部,由封装库的人来维护。
假设设计一个HYBProgressHUB的小例子,随手写的,没有真实写完整:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
typedef
NS_ENUM
(
NSUInteger
,
HYBProgressHUBMaskType
)
{
kHYBProgressHUBClear
,
kHYBProgressHUBBlack
,
kHYBProgressHUBUserEnabled
}
;
@interface
HYBProgressHUD
: NSObject
+
(
void
)
setDefaultMaskType
:
(
HYBProgressHUBMaskType
)
defaultMaskType
;
+
(
void
)
show
;
+
(
void
)
showWithText
:
(
NSString
*
)
text
;
+
(
void
)
showWithImage
:
(
NSString
*
)
image
;
+
(
void
)
showWithText
:
(
NSString
*
)
text
image
:
(
UIImage
*
)
image
;
+
(
void
)
showWithText
:
(
NSString
*
)
text
maskType
:
(
HYBProgressHUBMaskType
)
maskType
;
+
(
void
)
showWithImage
:
(
NSString
*
)
image
maskType
:
(
HYBProgressHUBMaskType
)
maskType
;
+
(
void
)
showWithText
:
(
NSString
*
)
text
image
:
(
UIImage
*
)
image
maskType
:
(
HYBProgressHUBMaskType
)
maskType
;
+
(
void
)
dismiss
;
+
(
void
)
dismissWithText
:
(
NSString
*
)
text
;
+
(
void
)
dismissWithImage
:
(
UIImage
*
)
image
;
+
(
void
)
dismissWithText
:
(
NSString
*
)
text
image
:
(
UIImage
*
)
image
;
@end
|
将单例放在实现文件中,并没有暴露出来:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@implementation
HYBProgressHUD
+
(
instancetype
)
sharedInstance
{
__block
HYBProgressHUD
*singleTon
=
nil
;
static
dispatch_once_t
onceToken
;
dispatch_once
(
&
onceToken
,
^
{
singleTon
=
[
[
[
self
class
]
alloc
]
init
]
;
}
)
;
return
singleTon
;
}
@end
|
因为根据不同的需求,不同的场景,show会有很多种形式,比如不可点击、可点击、是否半透明等,因此API也应该针对单一性原则提供对应的API。但是,一个App中大部分的HUD显示的样式、方式是相同的,因此,我们可以提供一个默认的全局的呈现样式,比如调用show这个API要显示的样式就是默认的样式,若不调用,内部也会给一个默认值。
对于dismiss也一样,可以是直接隐藏,也可以在隐藏之前先提示点什么,比如纯文本提示、纯图片提示、文本和图片组合提示等。
当然,我们还可以扩展API设置dismiss的默认显示图片和文字的样式:
1
2
3
4
5
6
7
8
9
10
|
typedef
NS_ENUM
(
NSUInteger
,
HYBProgressHUBDismissType
)
{
kHYBProgressHUBDismissSuccess
,
kHYBProgressHUBDismissFailed
,
kHYBProgressHUBDismissWarnings
,
kHYBProgressHUBDismissTimeout
}
;
+
(
void
)
dismissWithText
:
(
NSString
*
)
text
type
:
(
HYBProgressHUBDismissType
)
type
;
|
比如在所有的网络请求的地方,就可以写一个通用的API,在成功与失败回调处,统一处理显示不同的类型提示。
提示:
上面所描述内容为笔者纯手工尝试设计的,不代表足够合理,写出来的最主要目的是抛砖引玉,当然我更希望能够有大神出来指出其中的不足或者加以补充,让后来者少走弯路。