1.说一下OC的反射机制;
Class反射
// 通过类名的字符串形式实例化对象
Class class = NSSClassFromString(@“Person”);
Person *per = [[Person alloc]init];
// 将类名变为字符串
Class class1 = [Person class];
NSString *className = NSStringFromClass(class1);
// SEL的反射
// 通过方法的字符串形式实例化方法
SEL selector = NSSelectorFromString(@“setName”);
[per perfromSelector:selector withObject:@"Mike" afterDelay:1];
// 将方法变成字符串
NSString *str = NSStringFromSelector(@selector(setName:));
SEL:对方法的一种包装。包装的SEL类型数据它对应相应的方法地址,找到方法地址就可以调用方法。在内存中每个类的方法都存储在类对象中,每个方法都有个与之相对应的SEL数据类型,根据一个SEL数据就可以找到对应的方法地址,进而调用方法。
SEL sl = @selector(test); //将test方法包装成SEL对象。
SEL s2 = NSSelectorFromString(@"test"); // 将一个字符串方法转换成SEL对象
2.block的实质是什么?有几种block?分别是怎样产生的?
block:本质是一个object-c对象,一个结构体,里面有isa指针指向自己的类,有desc结构体描述block的信息。
存储位置: 代码去,堆区,栈区(ARC情况下会自动拷贝到堆区)
代码区: 不访问栈区的变量(如局部变量),且不访问堆区的变量(alloc), 此时的block存放在代码区。
堆区: 访问了处于堆区,或者栈区的变量,此时block就会存在堆区。
<font color=red>需要注意实际是放在栈区,在ARC情况下会自动拷贝到堆区,如果不是ARC则存放在栈区,所在函数执行完毕就回释放,想再外面调用需要用copy指向它,这样就拷贝到了堆区,strong属性不会拷贝、会造成野指针错区。</font>
3.__block修饰的变量为什么能在block里面能改变其值?
默认情况下,block里面的变量,拷贝进去的是变量的值,而不是指向变量的内存的指针。
__block 所起到的作用就是只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。进而在block内部也可以修改外部变量的值。
4.线程间通信?
NSThread 面向对象的,需要程序员手动创建线程,但不需要手动销毁。子线程间通信很难。
GCD c语言,充分利用了设备的多核,自动管理线程生命周期。比NSOperation效率更高。
NSOperation 基于gcd封装,更加面向对象,比gcd多了一些功能。
5.NSDictionary使用原理
1.NSDictionary(字典)是使用 hash表来实现key和value之间的映射和存储的, hash函数设计的好坏影响着数据的查找访问效率。
- (void)setObject:(id)anObject forKey:(id <NSCopying>)aKey;
2.Objective-C 中的字典 NSDictionary 底层其实是一个哈希表,实际上绝大多数语言中字典都通过哈希表实现,
6.什么是指针常量和常量指针?
指针常量: 本质就是一个常量,指针修饰,表示这个常量的值是一个指针。
int a,b;
int * const p=&a //指针常量
*p=9;//操作成功
p=&b;//操作错误
因为声明了是指针常量,那么说明指针地址不允许修改,但是指针指向的对象(内容)可以修改。
常量指针: 本质是指针,常量修饰,表示该指针是一个指向常量的指针。
int a,b;
const int *p=&a //常量指针
*p=9;//操作错误
p=&b;//操作成功
因为声明是常量指针,那么说明指针指向的常量值不允许修改,但是指针地址可以修改。
指向常量的指针常量
const int * const b = &a;
7.浏览器输入url到网页显示经历了什么过程?
1.输入网址
2.发送的DNS服务器,并获取域名对应的web的服务器对应的IP地址
3.与web服务器简历TCP连接
4.浏览器向web服务器发送http请求
5.web服务器响应请求,并返回指定URL的数据(或错误信息,或重定向的新的URL地址)
6.浏览器下载web服务器返回的数据及解析html源文件
7.生成DOM树,解析css和js,渲染界面,直到显示完成
8.获取APP是否是在后台
1. 通过监听AppDelegate的 applicationDidEnterBackground 方法来监听APP是否进入后台。
- (void)applicationDidEnterBackground:(UIApplication*)application{
NSLog(@"应用程序退出到后台");
}
2.UIApplication的属性applicationState 也可以知道状态。
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
typedef NS_ENUM(NSInteger, UIApplicationState) {
UIApplicationStateActive,
UIApplicationStateInactive,
UIApplicationStateBackground
} NS_ENUM_AVAILABLE_IOS(4_0);
9. performSelector: 与 method(直接调用)
正常来说,使用performSelector,会变成动态的检测是否允许调用某个selector,也就是说只有在运行时才会检测。method(直接调用)不一样,在编译时就会提示是否允许调用。
[objc testAction];
[objc performSelector:@selector(testAction)];
上述情况,两者是一致的。
performSelector: 支持调用指定的没有import的Selector 或者没有显示可调用的Selector
调用之前可以使用 respondsToSelector 检测一下是否可以调用
SEL aSelector = testAction();
[objc performSelector:aSelector];
SEL aSelector = NSSelecotrFromString(@"testAction");
[objc performSelector:aSelector];
Method(直接调用): 必须要在头文件中声明该方法的使用,也要将头文件import进来.
10.计算两个日期之间的天数
OC:
+ (NSInteger)daysBetweenDateWithFromDate:(NSDate*)fromDate toDate:(NSDate*)toDate{
NSDate *tmpFromDate;
NSDate *tmpToDate;
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar rangeOfUnit:NSCalendarUnitDay startDate:&tmpFromDate
interval:NULL forDate:fromDate];
[calendar rangeOfUnit:NSCalendarUnitDay startDate:&tmpToDate
interval:NULL forDate:toDate];
NSDateComponents *difference = [calendar components:NSCalendarUnitDay
fromDate:fromDate toDate:toDate options:0];
return [difference day];
}
swift:
extension NSDate {
func numberOfDaysUntilDateTime(toDateTime: NSDate, inTimeZone timeZone: NSTimeZone? = nil) -> Int {
let calendar = NSCalendar.currentCalendar()
if let timeZone = timeZone {
calendar.timeZone = timeZone
}
var fromDate: NSDate?, toDate: NSDate?
calendar.rangeOfUnit(.Day, startDate: &fromDate, interval: nil, forDate: self)
calendar.rangeOfUnit(.Day, startDate: &toDate, interval: nil, forDate: toDateTime)
let difference = calendar.components(.Day, fromDate: fromDate!, toDate: toDate!, options: [])
return difference.day
}
}
11.NSSet 与 NSArray
NSArray是一个有序的集合,NSSet是一个无序的集合。
查找某个对象时NSSet会比较快,因为NSSet是用hashkey来查找,而NSArray是通过遍历整体查找。
NSSet:
无序的,不重复的,快速插入,删除和查看某个元素.
NSArray:
有序,可以重复,通过脚标获取元素.
应用场景:
不需要排序,且不重复时,使用NSSet,例如: Cell的缓存池,不需要排序,随便取一个出来即可.
需要有序,访问指定位置的元素:NSArray, 例如tableView的数据源展示数据
12. id 和 instanceType 有什么区别?
相同点:
都是万能指针,指向对象。
不同点:
1.id 在编译时不能判断对象的真实类型,instanceType在编译时可以判断数据的真实类型。
2.id 可以修饰变量,返回值类型,形参,instanceType 只能作为返回值类型。
13. #import 跟#include 又什么区别,@class呢, #import<> 跟 #import“”又什么区别?
#import : oc的导入头文件的关键字,不会重复导入。
#include:c/c++导入的关键字。
@class:只导入声明,不导入实现。
14.如何对iOS设备进行性能测试?
Profile-> Instruments ->Time Profiler
15.atomic 修饰的属性是绝对安全的吗?
不是,所谓的安全只是局限于Setter 和 Getter 的访问器方法而言,
你对它进行release操作是不受影响的。但是这个时候就容易崩溃了。
16.UIView 和 CALayer 是什么关系?
UIView继承自UIResponder,而UIResponder是响应者对象,可以对iOS中的事件的响应及传递。
CALayer没有继承自UIResponder,所以不具备响应功。
CALayer 是 QuartzCore 中的类,是一个比较底层的用来绘制内容的类,用来绘制UI。
UIView对CALayer封装属性,对UIView进行frame,center等一些位置信息设置时,其实都是UIView对CALayer的一种封装,使得我们方便的控制控件位置。
但是我们对圆角,阴影,边框线等一些属性时,UIView就没有对齐封装,必须对layer进行设置。
UIView 是 CALayer 的代理,UIView 持有一个 CALayer 的属性,并且是该属性的代理,用来提供一些 CALayer 行的数据,例如动画和绘制。
17.Bounds 和 Frame 的区别?
Bounds:一般是相对于自身来说的,是控件的内部尺寸。如果你修改了 Bounds,那么子控件的相对位置也会发生改变。
Frame :是相对于父控件来说的,是控件的外部尺寸。
18.进程和线程的关系
线程是进程的执行单元,进程的所有任务都是在线程中执行。
线程是CPU调度和分配资源的最小单位。
一个进程可以有多个线程,但至少会有一个线程。
同一个进程内的线程可以共享资源。
19.什么是多线程?
多线程的实现原理:
事实上,同一时间内,单核cpu只能执行一个线程,多核cpu会在线程中来回带调度,从而造成多线程的现象。
如果是多核cpu那么就是处理多线程。
多线程的目的是为了同步完成多项任务,通过提高系统的资源利用率来提高系统的效率。
优点:
能适当提高程序的执行效率,资源利用率(CPU,内存利用率)
缺点:
开启线程需要一定的内存,线程过多会造成内存运用过大,线程越多,CPU的调度就会越多从而降低效率。
并行:
充分利用计算机的多核,在多个线程上同步进行.
并发:
在一条线程上通过快速切换,让人感觉在同步进行