例如:
1
2
3
|
NSArray
*array
=
@
[
@"12-11"
,
@"12-11"
,
@"12-11"
,
@"12-12"
,
@"12-13"
,
@"12-14"
]
;
|
参考答案:
1
2
3
4
5
6
7
8
9
10
11
12
|
NSMutableArray
*resultArray
=
[
[
NSMutableArray
alloc
]
initWithCapacity
:array
.
count
]
;
// 外层一个循环
for
(
NSString
*item
in
array
)
{
// 调用-containsObject:本质也是要循环去判断,因此本质上是双层遍历
// 时间复杂度为O ( n^2 )而不是O (n)
if
(
!
[
resultArray
containsObject
:item
]
)
{
[
resultArray
addObject
:item
]
;
}
}
NSLog
(
@"resultArray: %@"
,
resultArray
)
;
|
补充:原来集合操作可以通过valueForKeyPath来实现的,去重可以一行代码实现:
1
2
3
4
|
array
=
[
array
valueForKeyPath
:
@"@distinctUnionOfObjects.self"
]
;
NSLog
(
@"%@"
,
array
)
;
|
但是返回的结果是无序的,与原来的顺序不同。
1
2
3
4
5
6
7
8
|
NSMutableDictionary
*resultDict
=
[
[
NSMutableDictionary
alloc
]
initWithCapacity
:array
.
count
]
;
for
(
NSString
*item
in
array
)
{
[
resultDict
setObject
:item
forKey
:item
]
;
}
NSArray
*resultArray
=
resultDict
.
allValues
;
NSLog
(
@"%@"
,
resultArray
)
;
|
如果需要按照原来的升序排序,可以这样:
1
2
3
4
5
6
7
8
|
resultArray
=
[
resultArray
sortedArrayUsingComparator
:
^
NSComparisonResult
(
id
_Nonnull
obj1
,
id
_Nonnull
obj2
)
{
NSString
*item1
=
obj1
;
NSString
*item2
=
obj2
;
return
[
item1
compare
:item2
options
:NSLiteralSearch
]
;
}
]
;
NSLog
(
@"%@"
,
resultArray
)
;
|
1
2
3
4
5
|
NSSet
*set
=
[
NSSet
setWithArray
:array
]
;
NSArray
*resultArray
=
[
set
allObjects
]
;
NSLog
(
@"%@"
,
resultArray
)
;
|
如果要求有序,那就得排序,比如这里要升序排序:
1
2
3
4
5
6
7
8
|
resultArray
=
[
resultArray
sortedArrayUsingComparator
:
^
NSComparisonResult
(
id
_Nonnull
obj1
,
id
_Nonnull
obj2
)
{
NSString
*item1
=
obj1
;
NSString
*item2
=
obj2
;
return
[
item1
compare
:item2
options
:NSLiteralSearch
]
;
}
]
;
NSLog
(
@"%@"
,
resultArray
)
;
|
补充:
一直没有使用过有序集合,网友们反馈到可以直接使用有序集合,感谢大家:
1
2
3
4
|
NSOrderedSet
*set
=
[
NSOrderedSet
orderedSetWithArray
:array
]
;
NSLog
(
@"%@"
,
set
.
array
)
;
|
以上三种方法是笔者所能想到的办法。如果大家有更好的办法,欢迎在评论中指出。
1
2
3
|
NSArray、
NSSet、
NSDictionary与
NSMutableArray、
NSMutableSet、
NSMutableDictionary
|
参考答案:
特性:
作用:
参考答案:
笔者倾向于纯代码开发,所以所提供的参考答案可能具有一定的个人感情,不过还是给大家说说笔者的想法。
优点:
缺点:
2015-04-10格式化日期转为NSDate类型
参考答案:
1
2
3
4
5
6
7
8
9
|
NSString
*timeStr
=
@"2015-04-10"
;
NSDateFormatter
*formatter
=
[
[
NSDateFormatter
alloc
]
init
]
;
formatter
.
dateFormat
=
@"yyyy-MM-dd"
;
formatter
.
timeZone
=
[
NSTimeZone
defaultTimeZone
]
;
NSDate
*date
=
[
formatter
dateFromString
:timeStr
]
;
// 2015-04-09 16:00:00 +0000
NSLog
(
@"%@"
,
date
)
;
|
参考答案:
在iOS中,通常是通常UIWebView来实现,当然在iOS8以后可以使用WKWebView来实现.有以下几种实现方法:
1
2
3
|
-
(
BOOL
)
webView
:
(
UIWebView
*
)
webView
shouldStartLoadWithRequest
:
(
NSURLRequest
*
)
request
navigationType
:
(
UIWebViewNavigationType
)
navigationType
;
|
优缺点:
参考答案:
首先,我们要明确一点,同步和异步都是在线程中使用的。在iOS开发中,比如网络请求数据时,若使用同步请求,则只有请求成功或者请求失败得到响应返回后,才能继续往下走,也就是才能访问其它资源(会阻塞了线程)。网络请求数据异步请求时,不会阻塞线程,在调用请求后,可以继续往下执行,而不用等请求有结果才能继续。
区别:
参考答案:
在iOS中队列分为以下几种:
1
2
3
|
dispatch_queue
_
t
q
=
dispatch_queue_create
(
"..."
,
DISPATCH_QUEUE_SERIAL
)
;
|
1
2
3
|
dispatch_queue
_
t
q
=
dispatch_queue_create
(
"......"
,
DISPATCH_QUEUE_CONCURRENT
)
;
|
1
2
3
|
dispatch_queue
_
t
q
=
dispatch_get_global_queue
(
DISPATCH_QUEUE_PRIORITY_DEFAULT
,
0
)
;
|
1
2
3
|
dispatch_queue
_
t
q
=
dispatch_get_main_queue
(
)
;
|
上面这四种是针对GCD来讲的,串行队列中的任务只能一个个地执行,在前一个没有执行完毕之前,下一个只能等待。并行队列可以并发地执行任务,因此多个任务之间执行的顺序不能确定,当添加一个新的任务时,交由GCD来判断是否要创建新的新的线程。
大家可以阅读图片多线程,也许更明了:
参考答案:
内存管理准则:谁强引用过,谁就在不再使用时使引用计数减一。
对于内存的使用和优化常见的有以下方面:
参考答案:
plist是iOS系统中特有的文件格式。我们常用的NSUserDefaults偏好设置实质上就是plist文件操作。plist文件是用来持久化存储数据的。
我们通常使用它来存储偏好设置,以及那些少量的、数组结构比较复杂的不适合存储数据库的数据。比如,我们要存储全国城市名称和id,那么我们要优先选择plist直接持久化存储,因为更简单。
参考答案:
参考答案:
数据库的简单操作,还是会的,大学可没白学。
增:
1
2
3
|
insert
into
tb_blogs
(
name
,
url
)
values
(
'标哥的技术博客'
,
'http://www.henishuo.com'
)
;
|
删:
1
2
3
|
delete
from
tb_blogs
where
blogid
=
1
;
|
改:
1
2
3
|
update
tb_blogs
set
url
=
'www.henishuo.com'
where
blogid
=
1
;
|
查:
1
2
3
|
select
name
,
url
from
tb_blogs
where
blogid
=
1
;
|
12、在提交苹果审核时,遇到哪些问题被拒绝,对于被拒绝的问题是如何处理的。
参考答案:
对于笔者而言,所提交过的app还没有被拒绝过。不过在笔者所维护的几个群里经常有朋友们问到相关被拒绝的解决办法。幸好还懂一点点英文,还能帮助他们翻译翻译苹果反馈的被拒原因及所担的建议。
这里只列出几种最常见的被拒原因:
感谢标哥整理的试题~
这题看不清楚,不过可以看得出来是编程规范问题。所以呢,笔者也就没有办法说明哪些不合理了。不过笔者曾经为公司的出过一个编程规范文档,后来整理成文章GIF图分享给大家。
参考答案:
下面是笔者通过打印,先出现ViewController,然后在点击ViewController上的按钮时,模态弹出了一个纯代码HYBViewController,其打印如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
-
[
ViewController
initWithCoder
:
]
-
[
ViewController
loadView
]
-
[
ViewController
viewDidLoad
]
-
[
ViewController
viewWillAppear
:
]
-
[
ViewController
viewDidAppear
:
]
// present HYBViewController
-
[
HYBViewController
initWithNibName
:bundle
:
]
-
[
HYBViewController
init
]
-
[
HYBViewController
loadView
]
-
[
HYBViewController
viewDidLoad
]
-
[
ViewController
viewWillDisappear
:
]
-
[
HYBViewController
viewWillAppear
:
]
-
[
HYBViewController
viewDidAppear
:
]
-
[
ViewController
viewDidDisappear
:
]
|
生命周期如下:
1
2
3
4
5
6
7
8
9
|
*
xib
/
storyboard:
-
initWithCoder
:,而非
xib
/
storyboard的是
-
initWithNibName
:bundle
:然后
-
init
*
-
loadView
*
-
viewDidLoad
*
-
viewWillAppear
:
*
-
viewDidAppear
:
*
-
viewWillDisappear
:
*
-
viewDidDisappear
:
|
注意,当从ViewController进入到HYBViewController控制器时,注意出现顺序如下:
1
2
3
4
5
6
|
*
-
[
ViewController
viewWillDisappear
:
]
*
-
[
HYBViewController
viewWillAppear
:
]
*
-
[
HYBViewController
viewDidAppear
:
]
*
-
[
ViewController
viewDidDisappear
:
]
|
在HYBViewController完全出现后,才会调用前一个控制器的完全消失。像这种要不同控制器之间导航条隐藏与显示控制问题,就需要特别注意其生命周期的顺序。
注意:有朋友说这里是错的,不过笔者打印出来验证发现就是这样的顺序!用模态呈现测试!截图如下:
请大家看看哪里出错!
参考答案:
1
2
3
4
5
|
imgView
.
layer
.
cornerRadius
=
10
;
// 这一行代码是很消耗性能的
imgView
.
clipsToBounds
=
YES
;
|
好处是使用简单,操作方便。坏处是离屏渲染(off-screen-rendering)需要消耗性能。对于图片比较多的视图上,不建议使用这种方法来设置圆角。通常来说,计算机系统中CPU、GPU、显示器是协同工作的。CPU计算好显示内容提交到GPU,GPU渲染完成后将渲染结果放入帧缓冲区。
简单来说,离屏渲染,导致本该GPU干的活,结果交给了CPU来干,而CPU又不擅长GPU干的活,于是拖慢了UI层的FPS(数据帧率),并且离屏需要创建新的缓冲区和上下文切换,因此消耗较大的性能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
-
(
UIImage
*
)
hyb_imageWithCornerRadius
:
(
CGFloat
)
radius
{
CGRect
rect
=
(
CGRect
)
{
0.f
,
0.f
,
self
.
size
}
;
UIGraphicsBeginImageContextWithOptions
(
self
.
size
,
NO
,
UIScreen
.
mainScreen
.
scale
)
;
CGContextAddPath
(
UIGraphicsGetCurrentContext
(
)
,
[
UIBezierPath
bezierPathWithRoundedRect
:rect
cornerRadius
:radius
]
.
CGPath
)
;
CGContextClip
(
UIGraphicsGetCurrentContext
(
)
)
;
[
self
drawInRect
:rect
]
;
UIImage
*image
=
UIGraphicsGetImageFromCurrentImageContext
(
)
;
UIGraphicsEndImageContext
(
)
;
return
image
;
}
|
然后调用时就直接传一个圆角来处理:
1
2
3
|
imgView
.
image
=
[
[
UIImage
imageNamed
:
@"test"
]
hyb_imageWithCornerRadius
:
4
]
;
|
这么做就是on-screen-rendering了,通过模拟器->debug->Color Off-screen-rendering看到没有离屏渲染了!(黄色的小圆角没有显示了,说明这个不是离屏渲染了)
1
2
3
4
5
6
7
8
|
-
(
void
)
drawRect
:
(
CGRect
)
rect
{
CGRect
bounds
=
self
.
bounds
;
[
[
UIBezierPath
bezierPathWithRoundedRect
:rect
cornerRadius
:
8.0
]
addClip
]
;
[
self
.
image
drawInRect
:bounds
]
;
}
|
这里不细说了,个人感觉不如第二种好用、通用
若有更多,可以在评论中指出~
参考答案:
iOS使用hit-testing寻找触摸的view。 Hit-Testing通过检查触摸点是否在关联的view边界内,如果在,则递归地检查该view的所有子view。在层级上处于lowest(就是离用户最近的view)且边界范围包含触摸点的view成为hit-test view。确定hit-test view后,它传递触摸事件给该view。
官方小例子事件响应者链如下图所示:
Hit-test view是处理触摸事件的第一选择,如果hit-test view不能处理事件,该事件将从事件响应链中寻找响应器,直到系统找到一个处理事件的对象。若不能处理,则就有事件传递链了,继续看下面的事件传递链。
事件传递链如下图所示:
左半图:
右半图:
为了解答这个小题目,翻阅了官方文档,由于内容较多,这里不说那么多,若要了解更多,参考官方文档吧:Event Handling Guide for iOS
参考答案:
关于block循环引用问题是非常常见的,但是很多人没有深入研究过,xcode没有提示警告就以为没有形成循环引用了。笔者也见过很多高级iOS开发工程师的同事,使用block并不会分析是否形成循环引用。
参考答案:
说到NSTimer这个定时器类,要使用好它,还得了解Run Loop,因为在不同的run loop mode下,定时器不都会回调的。
mode主要是用来指定事件在运行循环中的优先级的,分为:
苹果公开提供的Mode有两个:
如果我们把一个NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环中的时候, ScrollView滚动过程中会因为mode的切换,而导致NSTimer将不再被调度。当我们滚动的时候,也希望不调度,那就应该使用默认模式。但是,如果希望在滚动时,定时器也要回调,那就应该使用common mode。
如果想要销毁timer,则必须先将timer置为失效,否则timer就一直占用内存而不会释放。造成逻辑上的内存泄漏。该泄漏不能用xcode及instruments测出来。 另外对于要求必须销毁timer的逻辑处理,未将timer置为失效,若每次都创建一次,则之前的不能得到释放,则会同时存在多个timer的实例在内存中。
参考答案:
笔者不是很清楚题目的真正要求,是想知道核心动画的哪些知识点。如何开始和结束动画,这核心动画有很多种,每种动画还有很大的区别。
参考答案:
动画的开始和结束都可以通过CAMediaTiming协议来处理,核心动画的基类是遵守了CAMediaTiming协议的,可以指定动画开始时间、动画时长、动画播放速度、动画在完成时的行为(停留在结束处、动画回到开始处、动画完成时移除动画)。