iOS - 常见问题总结

227 阅读8分钟
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 abint * const p=&a //指针常量
*p=9;//操作成功
p=&b;//操作错误
因为声明了是指针常量,那么说明指针地址不允许修改,但是指针指向的对象(内容)可以修改。

常量指针: 本质是指针,常量修饰,表示该指针是一个指向常量的指针。
int abconst 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。

UIViewCALayer封装属性,对UIView进行frame,center等一些位置信息设置时,其实都是UIViewCALayer的一种封装,使得我们方便的控制控件位置。
但是我们对圆角,阴影,边框线等一些属性时,UIView就没有对齐封装,必须对layer进行设置。

UIViewCALayer 的代理,UIView 持有一个 CALayer 的属性,并且是该属性的代理,用来提供一些 CALayer 行的数据,例如动画和绘制。

17.Bounds 和 Frame 的区别?
Bounds:一般是相对于自身来说的,是控件的内部尺寸。如果你修改了 Bounds,那么子控件的相对位置也会发生改变。

Frame :是相对于父控件来说的,是控件的外部尺寸。
18.进程和线程的关系
线程是进程的执行单元,进程的所有任务都是在线程中执行。
线程是CPU调度和分配资源的最小单位。
一个进程可以有多个线程,但至少会有一个线程。
同一个进程内的线程可以共享资源。
19.什么是多线程?
多线程的实现原理:
事实上,同一时间内,单核cpu只能执行一个线程,多核cpu会在线程中来回带调度,从而造成多线程的现象。
如果是多核cpu那么就是处理多线程。
多线程的目的是为了同步完成多项任务,通过提高系统的资源利用率来提高系统的效率。
优点:
能适当提高程序的执行效率,资源利用率(CPU,内存利用率)
缺点:
开启线程需要一定的内存,线程过多会造成内存运用过大,线程越多,CPU的调度就会越多从而降低效率。
并行:
充分利用计算机的多核,在多个线程上同步进行.
并发:
在一条线程上通过快速切换,让人感觉在同步进行