iOS基础-高级进阶面试题
阅读原文时间:2021年04月20日阅读:1

1、OC 语言的基本特点

OC 语言是 C 语言的一个超集,只是在 C 的基础之上加上了面向对象(oop) 的特性; 

OC 与 Java 语言相同都是单继承,这一点与 C++语言不同(多重继承);

OC 不支持命名空间机制,取而代之的是在类名之前添加前缀,以此来区分。 

2、以下命名正确的是

(1)类 (Person、person、ObjectAndKeys、personAndOther) (2)对象 (objectAndKeys、Person、_person、$dog) (3)实例变量 (_dog、^age、name、Name、) 

3、数据类型和表达式

(1)inta=5,b=2,c=2,result,result=a*b++-c; 求result的值和b 的值。 

答:result 的值为 8,b 的值为 3。根据运算符的优先级(乘法比加法的优先级高),先计算 a*b,因为 b++,“++”在后面。因此,先计算 a*b,其为 10,计算完成后 b 的结果++,因此 b 的结果为 3。最后与 c 相减,result 的结果 为8。 

(2)result = (b > a)? a++ :((++c > a-b)? ++c : b++),求 result,a,b,c 的值,假设 a、b、c 的值分别为 1、2、3。(右结合性;运算符号优 先级->结合性->顺序) 

答:运算符号相同,因此判断运算符号的结合性。即表达式为:result = (b > a)? a++:(++c > a-b)? ++c : b++),然后我们判断表达是(b > a)是否为真。因此,result 的结果为 1,a、b、c 的值分别为 2、2、3。 

(3)int a = 5, b = 12, c = 3, result= 0, d = 5, e = 2,result = a -= b /= c += d %= e;求 result,a、b、c、d、e。 

答:运算符号相同,因此,判断该运算符号的结合性。赋值运算符号为右结合, 因此,表达式从右开始计算。d %=2,分解为 d = d % e,d 值为 1;计算 c += d, 同理,c值为4。再计算b /= c,b值为3;再计算a -=b,a值为2。最后将a 值赋值给 result。因此,result 的结果为 2,a、b、c、d、e 的结果分别是 2、3、 4、1、2。(复合赋值运算符效率更高) 

 (4)如果第三题中d为-5,求 result。答:%(模运算符号)的符号取决与第一个数,因此,result的值为-1,a、b、 

c、d、e 的值分别为-1、6、2、-1、2。
(5)假设 a、b、c 的值分别是 4、5、6。那么 result = a < b < c,求 result 是多少? 答:result 值为 1。

(6)解释 id 类型          

答:任意类型对象。程序运行时决定才对象的类型。

(7)解释 nil,发送消息时,程序是否会出现异常。 

答:不会,在 OC 语言中可以 nil 发送消息,而程序不会抛出异常,其结果是什么也不做。 

4、流程控制语句

(1)switch 语句每一 case 都需要添加 break 语句吗?

答:switch 语句中的 break 语句不是必须的,此外,default语句也不是必须 

添加的。如果在某一个条件中添加(case 语句之后)break 语句,即当条件满足时,跳出 switch 语句。

(2)do while 语句和 while 语句的区别,并写出几个死循环。 

答:do while 语句至少执行一次循环体,而 while 语句括号中的表达式为真,才执行循环体。 

while(1){ }、for(;)

(3)switch 语句 if 语句区别与联系以及它的优势在哪里 

答:均表示条件的判断,switch 语句表达式只能处理是整型、字符型和枚举类 型,而选择流程语句则没有这样的限制。但 switch 语句比选择流程控制语句效 率更高。

(4)int number = 26,k = 1,求 k 的值   do {
k *= number %10;     number /= 10;   
} while(number); 

答:do while 语句的特点是,循环体至少执行一次。程序执行到表达式 k*=number%10,已知 number 为 26,又已知算术运算符比赋值运算符好优先 级别高,因此先计算 number%10,其结果为 6;已知 k 为 1,因此,k 的结果为 6。number/=10,number的值 2。while 语句判断表达式是否为真,此时,number 为 2。继续执行循环体,此时 number、k 的值分别为 2、6,2%10 的结果仍为 2,再与 k 相乘,其 k 的结果为 12。程序执行到循环体第二行 number/10,此 时 number 已为 10,因此,number的结果为 0。while 表达式内条件为假,循 环就此结束。因此,k 的值为 12。 

5、写出以下方法类型、方法名称和返回值类型

(1)-(void)initWithName:(NSString*)name andAge:(int) age;
(2)+(Person*)personName:(NSString)name; (3)-(void)setName:(NSString *)name setAge:(int)agesetDelegate:(id)delegate; 

(4)-(NSString *)name;     (5)+ (Kingdom *)shareKingdom; (6)+(Kingdom *)defaultKingdom; 

6、创建一个这样的 Person 类,用类目的形式给 Person 添加一组方法(方法任意)、并且若干私有方法以及在 Person 类中添加一个协议(手写代码)

.h文件
#import @”Person.h” 

@protocol PersonDelegate @required

- (void)thisRequiredMethod;

@optional 

- (void)thisOptionalMethod;@end 

@interface Person : NSObject{ @private 

NSString *_name; 

NSInteger _age; } 

-  (void)test;

-  (void)test1:(int)arg1;

-  (void)test1:(int)arg1test2:(int)arg2; @end@interface Person (Create)

- (id)initWithName:(NSString*)aName;

- (id)initWithName:(NSString*)aName age:(int)age; + (id)personBorn; 
@end

.m文件 

@interface Person () -(void)private1;

- (void)private2; @end@implementation 

- (void)test {}

- (void)test1:(int)arg1 {}

- (void)test1:(int)arg1test2:(int)arg2 {} - (void)private1 {}

- (void)private2 {} 

- (id)initWithName:(NSString*)aName { self = [super init]; 

if (self) {}

return self; }

- (id)initWithName:(NSString*)aName age:(int)age { 

…… } 

+ (id)personBorn {

Person *person = [[Personalloc] init]; return [person autorelease]; 

} @end 

7、协议的基本概念和协议中方法默认为什么类型?通知、Block概念、使用环境、三者的区别?

协议:OC 中的协议是一个方法列表。它的特点是可以被任何类使用(实现),但它并不是类(这里我们需要注意),自身不会实现这样方法,而是由其他人来实现。

协议经常用来实现委托对象(委托设计模式)。 如果一个类采用了一个协议,那么它必须实现协议中必须需要实现的方法,在 协议中的方法默认是必须实现(@required),添加关键字@optional,表明一旦采用该协议,这些“可选”的方法是可以选择不实现的。

三者区别:

    代理是一对一的,对用一个协议,一个对象只能设置一个代理,所以单例对象就不能用代理;代理更注重过程的信息的传输,比如发起一个网络请求,可能想要知道此时请求是否已经开始、是否收到了数据、数据是否已经接收完成、数据接收是否失败;代理的方法是分离开的,并不会引用上下文,可以有效的避免循环引用;声明代理时为了防止循环引用应该使用assign或者weak;

    block(闭包)它和代理一样,一般都是一对一之间交互通信,他能够直接存储一个代码块的实现部分,而不需要去定义一个函数;写法简单、不需要写protocol、函数等等;比较注重结果的传输,比如对于一个事件,只想知道成功或者失败,并不知道进行了多少或者额外的一些信息;需要注意防止循环引用(block在多次调用时,需要长期存储,就很容易引起循环引用问题);

    通知它可以一对多,在远距离传值时比较有优势;在通知注册完回调方法之后,需要对注册进行取消操作,也就是移除通知,如果通知回调了已经被回收的类里面的方法,就会带来调用上的问题,莫名的闪退;

8、#include 与#import 的区别、#import 与@class 的区别

#include c语言中引入一个头文件,但是可能出现交叉编译,

OC里面已经没有这个方式引入头文件了,统一使用#import

#import在OC中引入自己创建的头文件           

#import””是引入自己创建类的头文件             

#import<>是引入系统类的头文件                

#import不会出现交叉编译

@class对一个类进行声明,告诉编译器有这个类,但是类的定义什么的都不知道.

9、@public、@protected、@private 它们的含义与作用

( 1) @public: 答:对象的实例变量的作用域在任意地方都可以被访问 

( 2) @protected: 答:对象的实例变量作用域在本类和子类都可以被访问 

( 3) @private: 答:实例变量的作用域只能在本类(自身)中访问 

( 4) 通过指针运算符(->)能够访问到private 方法吗? OC 语言中还提供哪些方式能直接和间接的访问对象实例变量? 

答:不可以,可以通过合成存取器访问实例变量,也可自己定义 setter 和 getter 方法访问实例变量,KVC(key value coding)——键值编码,间接的方式访问实例变量。 

10、简述类目(延展)优点和缺点,如果覆盖本类或者父类的方法,会出现什么问题?

答:(1)优点:不需要通过增加子类而增加现有类的行为(方法),且类目中 的方法与原始类方法基本没有区别;通过类目可以将庞大一个类的方法进行划分,从而便于代码的日后的维护、更新以及提高代码的阅读性。 

(2)缺点:无法向类目添加实例变量,如果需要添加实例变量,只能通过定义子类的方式;类目中的方法与原始类以及父类方法相比具有更高级别的优先级,如果覆盖父类的方法,可能导致super 消息的断裂。因此,最好不要覆盖原始类中的方法。 

11、简述内存管理基本原则

答:(1)如果使用 alloc、copy(mutableCopy)或者retian 一个对象时,你就 有义务,向它发送一条 release 或者 autorelease 消息。其他方法创建的对象,不 需要由你来管理内存。 

(2)向一个对象发送一条 autorelease 消息,这个对象并不会立即销毁, 而是将这个对象放入了自动释放池,待池子释放时,它会向池中每一个对象发送 一条 release 消息,以此来释放对象。 

(3)向一个对象发送 release 消息,并不意味着这个对象被销毁了,而是 当这个对象的引用计数为 0 时,系统才会调用 dealloc 方法,释放该对象和对象 本身它所拥有的实例。 

12、在 objective c 中是否支持垃圾回收机制?

答:OC是支持垃圾回收机制的(Garbage collection简称GC),但是apple的移动终端中,是不支持 GC 的,Mac 桌面系统开发中是支持的。

13、什么是 ARC 技术?与 GC 是否相同?

答:ARC 是 Automatic Reference Counting 的简称,我们称之为自动引用计数, 

是在IOS 5之后推出的新技术,它与GC的机制是不同的。我们在编写代码时, 不需要向对象发送 release 或者 autorelease 方法,也不可以调用 delloc 方法, 编译器会在合适的位置自动给用户生成 release 消息(autorelease),ARC 的特 点是自动引用技术简化了内存管理的难度。 

14、什么是 retain count?

答:每一个对象都默认有一个 retainCount 的属性,数值的多少表示现在有几 

个实例正在引用它。当它为 0 时,系统会自动调用 dealloc 方法,将内存回收。

15、写出@property (nonatomic ,retain) Person*person;

@synthesize person 具体实现,并指出其中含义。 

非原子性事物 

-  (void)setPerson:(Person*)person { 
if (_person != person){ [_person release]; _person = [personretain]; } 
}

-  (Person *)person {
return _person; } 
原子性事物
-(void)setPerson:(Person *)person {

@synchronized(self) { 
if(_person != person){ 

[_person release]; _person =[person retain]; } 

} } 

- (Person *)person { 

@synchronized(self) { 

return _person; } } 

16、深、浅复制的基本概念以及他们的区别

深复制拷贝的是对象的具体内容,并为其分配新的内存地址,拷贝结束后,两者的内存地址是不一样的,两个对象也互不影响,互不干涉;

浅复制拷贝的是内存地址,让目标对象指针和源对象指向同一片内存空间,当内存销毁的时候,指向这篇内存的指针需要重新定义才可以使用,要不然会成为野指针;

深拷贝拷贝的是内容,浅拷贝拷贝的是指针。深拷贝和浅拷贝最大的区别就是子类对象的地址是否改变,如果子类对象的地址改变那么就是深拷贝。

17、堆和栈的区别

  (1)栈区(stack)由编译器自动分配释放 ,存放方法(函数)的参数值, 局部变量的值等。先进后出。 

  (2)堆区(heap)一般由程序员分配释放, 若程序员不释放,程序结束时由OS回收。 

  (3)全局区(静态区)(static),全局变量和静态变量的存储是放在一块 的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后有系统释放。 

  (4)文字常量区—常量字符串就是放在这里的。程序结束后由系统释放 (5)程序代码区—存放函数体的二进制码代;

18、用户自定义了一个对象,如何实现拷贝(可变和不可变拷贝)

答:必须实现 copying 和 mutableCopying 协议,表示返回一个不可变和可变的 对象。否则,程序将会出现异常。

- (id)copyWithZone:(NSZone*)zone
{ 

Person *person = [[selfClass] allocWithZone:zone]; person ->age = self.age;

person ->name = self.name;

return person; 

}

-(id)mutableCopyWithZone(NSZone *)zone; 

19、以下代码有问题吗?如果有,会出现什么问题?

- (void) setName:(NSString*)name { 

self.name = name; } 

答:引起重复调用(自己调用自己)。 

20、定义属性时,什么时候用 assign、retain、copy 以及它们的之间的区别?

答:(1)assign:普通赋值,一般常用于基本数据类型,常见委托设计模式, 以此来防止循环引用。(我们称之为弱引用,weak) 

(2)retain:保留计数,获得到了对象的所有权。引用计数在原有基础上加1。 

(3)copy:一般认为,是在内存中重新开辟了一个新的内容空间,用来存储新的对象,和原来的对象是两个不同的地址,引用计数分别 1。但是当 copy 对象为不可变对象时,那么 copy 的作用相当于retain。因为,这样可以节约内 存空间。 

21、解释以下关键字, static、self、super 用实例说明

答 static: 静态全局变量,持久性作用、存储区域在静态区域,它的生命周期和应用进行绑定。程序结束时,由系统自动回收。

优点: 1、节省内存。静态变量只存储一处,但供所有对象使用。         

2、它的值是可以更新的。

3、可提高时间效率。只要某个对象对静态变量更新一次,所有的对象都能访问更新后的值。

self:当前消息的接收者。

super:向父类发送消息。 

22、解释 self = [super init]方法

答:容错处理,当父类初始化失败,会返回一个 nil,表示初始化失败。由于继承的关系,子类是需要拥有父类的实例和行为的,因此,我们必须先初始化父类,然后再初始化子类。 

23、当我们释放对象时,为什么需要调用[superdealloc] 方法?

(1)因为,子类是继承自父类,那么子类中有一些实例变量(对象),是继承子父类的,因此,我们需要调用父类方法,将父类所拥有的实例进行释放。 

(2)先将子类所拥有的实例进行释放,然后再释放父类的。

24、objective-c 有私方法么?私有变量呢?

是有的,我们称之为延展。私有变量也是有的(@private)。

25、以下每行代码执行后,person 对象的retain count 分别是多少?

Person *person = [[Personalloc] init]; // 1               

[person retain]; [personrelease]; [person release]; 

// 2 // 1 

// 0 

26、在某个方法中 self.name = _name 、name = _name 他们有区别吗,为什么?

是有区别的,前者是存在内存管理的,它会对_name 对象进行保留或者拷 贝操作,而后者是普通赋值。 

27、假设我们写了一个类的合成存取器,@property (nonatomic, copy)NSString *name;@synthesize name;

(1)NSString *aName =[NSString stringWithFormat:@”a”];
person.name = aName 此时 name 的引用计数是几,为什么,这么做 有什么好处?

答:它的引用技术是 2,相当于 retain 操作。 

(2)NSMutableString*aName =[NSMutableString stringWithFormat:@”a”]; 同上 

答:它的引用技术是 1,真正意义上的拷贝。

(3)返回这一个字符串的类型,是可变的吗?如果不是,为什么?我们又如何做?       

答:不可变,因为合成存取器中用的 copy。如果,我们需要返回一个可变的字符串时,那么必须自己实现 setter 和getter 方法。 

28、自动释放池是什么,如何工作?

答:自动释放池是 NSAutorelease 类的一个实例,当向一个对象发送 autorelease 消息时,该对象会自动入池,待池销毁时,将会向池中所有对象发送一条release 消息,释放对象。[pool release];[pool drain]表示的是池本身不会销毁,而是池子中的临时对象都被发送 release,从而将对象销毁。

29、为什么 delegate(代理)属性都是 assign 而不是 retain的?

答:防止循环引用,以至对象无法得到正确的释放。 

30、iOS 开发中数据持久性,有哪几种。

答:文件写入、对象归档、sqlite3 数据库、coredata 

31、对象归档的基本概念,以及它的特点是什么?

答:归档为对象的数据持久化提供了一种解决方法,它特点是给归档的对象进 行加密,增强了数据的安全性。此外,自定义类的对象归档必须实现 NSCoding 协议。 

32、什么是谓词?
答:cocoa 中提供了一个 NSPredicate 的类,该类主要用于指定过滤器的条件, 

每一个对象通过谓词进行筛选,判断条件是否匹配。

33、什么是 KVC 和 KVO?以及它们之间的关系是什么

答:(1)KVC(键值编码)是一种间接访问对象实例变量的机制,该机制可以不通过存取方法就可以访问对象的实例变量。非对象类型的变量将被自动封装或者解封成对象。此外,使用 KVC 能够简化代码。我们需要注意 KVC 有两个较为 明显的缺点,一旦使用 KVC 你的编译器无法检查出错误,即不会对设置的键、 键路径进行错误检查,且执行效率要低于(虽然效率已经很高,你已经感觉不到) 合成存取器方法和自定的 setter 和 getter 方法。因为使用 KVC 键值编码,它必 须先解析字符串,然后在设置或者访问对象的实例变量。 

(2)KVO(键值观察)是一种能使得对象获取到其他对象属性变化的通知机制。

(3)实现 KVO 键值观察模式,被观察的对象必须使用 KVC 键值编码来修改它的实例变量,这样才能被观察者观察到。因此,KVC 是 KVO 的基础或者说KVO 的实现是建立在 KVC 的基础之上的。 

34、在 objective c 中如何实现KVO

答:(1)注册观察者(这里我们需要注意,观察者和被观察者不会被保留也不 会被释放) 

- (void)addObserver:(NSObject*)observer forKeyPath:(NSString *)keyPath 

options:(NSKeyValueObservingOptions)options 

context:(void *)context; 

(2)接收变更通知


  • (void)observeValueForKeyPath:(NSString*)keyPath 

ofObject:(id)objectchange:(NSDictionary *)change context:(void *)context; 

(3)移除对象的观察者身份


  • (void)removeObserver:(NSObject*)observer 

forKeyPath:(NSString*)keyPath; 

35、当我们释放我们的对象时,为什么需要调用[superdealloc]方法,它的位置又是如何的呢?

答:因为子类的某些实例是继承自父类的,因此需要调用[super dealloc]方法, 来释放父类拥有的实例,其实也就是子类本身的。一般来说我们优先释放子类拥有的实例,最后释放父类所拥有的实例。 

36、以下代码会出项问题吗?如果有,我们又该如何修改?

@property (nonatomic, copy)NSMutableString *name; @synthesize name = _name; 

self.name = [NSMutableStringstringWithFormat:@"..xyz"]; [self.name insertString:@"abc"atIndex:0]; 

答:不可变字符串不可以被修改,可以通过自定义 set 方法,将字符串的拷贝改为可变的拷贝。 

37、当我们将一个对象加入自动释放池时,该对象何时被销毁?

答:我们在 application kit 应用程序中,自动释放池中的临时对象被销毁的时间时,一个事件循环结束后。注意自动释放池没有被释放,而是被排空了,向池 发送了 drain 消息。 

38、当我们调用一个静态方法时,需要对对象进行release 吗?

答:不需要,静态方法(类方法)创建一个对象时,对象已被放入自动释放池。在池被释放时,很有可能被销毁。 

39、什么叫键路径?

答:在一个给定的实体中,同一个属性的所有值具有相同的数据类型。 键-值编码技术用于进行这样的查找—它是一种间接访问对象属性的机制。键路径是一个由用点作分隔符的键组成的字符串,用于指定一个连接在一起的对 象性质序列。第一个键的性质是由先前的性质决定的,接下来每个键的值也是对于其前面的性质。键路径使您可以以独立于模型实现的方式指定相关对象的性 质。通过键路径,您可以指定对象图中的一个任意深度的路径,使其指向相关对 象的特定属性。 

40、以下代码存在内存泄露吗?如果有,如何去修改

self.object = [NSObjectalloc] init]; self.object = [NSObject object] retain]; self.object = [NSObjectobject]; 

- (void)dealloc { 

self.object = nil; 

[super dealloc]; } 

41、循环引用是什么,如何解决这样的问题

答:对象a创建并引用到了对象b;对象b创建并引用到了对象c;对象c创建并引用到了对象b。这时候b和c的引用计数分别是2和1。   

 当a不再使用b,调用release释放对b的所有权,因为c还引用了b,所以b的引用计数为1,b 不会被释放。b不释放,c 的引用计数就是 1,c 也不会被释放。从此,b 和 c 永远留在内存 中。这种情况,必须打断循环引用通过其他规则来维护引用关系。我们常见的 delegate 往往是 assign 方式的属性而不是 retain 方式 的属性, 赋值不会增加引用计数,就是为了防止 delegation 两端产生不必要的循环引用。 

42、isMemberOfClass 和isKindOfClass 联系与区别

答:两者都能检测一个对象是否是某个类的成员, 两者之间的区别是: 

isKindOfClass 不但可以用来确定一个对象是否是一个类的成员,也可以用来确 定一个对象是否是派生自该类的类的成员 ,而 isMemberOfClass 做不到后一点。 

如 ClassA 派 生 自 NSObject 类 , ClassA *a = [ClassA alloc]init]; [a isKindOfClass:[NSObject class]] 可以检查出 a 是否是 NSObject 派生类 的成员,但isMemberOfClass 做不到。

43.简述OC中内存管理机制

   1.1 OC的内存管理机制是自动引用计数,内存管理的原则是谁开辟谁释放,有retain的地方就要有release

   1.2 内存管理分为ARC和MRC,在MRC下我们需要手动管理内存,需要使用到retain/copy/release/autorelease等方法实现内存管理。ARC下则一般不需要我们手动管理,系统会在适当的位置加上内存管理关键字。

   1.3 retain是引用计数+1, 在内存管理中, 要记得内存管理原则: 谁开辟谁释放, 有retain就要有release.  release是引用计数-1. alloc匹配的是dealloc, alloc是开辟内存空间, dealloc是销毁所开辟的内存, 有开辟就要有销毁.

44.readwrite,readonly,assign,retain,copy,nonatomic 、atomic、strong、weak属性的作用?

readWrite读写特性,可读可写.

readonly只读, 只有getter, 没有setter.

assign一般用于基本数据类型和ID类型.

copy拷贝, 一般用于 NSString 分为深拷贝和浅拷贝, 深拷贝拷贝的是对象, 浅拷贝拷贝的是指针. nonatomic非原子性, 不考虑线程安全, 优点是效率高.

atomic原子性, 有点是线程安全, 缺点是效率低.

strong强引用, 和MRC下的retain一样. weak弱引用, 类似MRC下的assign. 但是要注意的是strong和weak都是修饰对象类型的属性的, 不能修饰基本数据类型. ARC下仍然使用assign修饰基本数据类型. 

45.关于iOS多线程问题

1、NSThread(两种创建方式)

[NSThreaddetachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil];

NSThread *myThread =[[NSThread alloc] initWithTarget:self selector:@selector(doSomething:)object:nil];

[myThread start];

2、NSOperationQueue

NSOperationQueue*oprationQueue=[[NSOperationQueuealloc] init];

oprationQueueaddOperationWithBlock:^{

//这个block语句块在子线程中执行

}

http://alloc.sinaapp.com/wp/?p=237

3、GrandCentral Dispatch (GCD)

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

//耗时的操作

dispatch_async(dispatch_get_main_queue(),^{

//更新界面

});

});

http://blog.csdn.net/totogo2010/article/details/8016129

PS:不显示的创建线程的方法:

用NSObject的类方法performSelectorInBackground:withObject:创建一个线程:[Obj performSelectorInBackground:@selector(doSomething)withObject:nil];

多线程是一个比较请谅解的方法来实现单个应用程序内多个代码执行路径,

三种多线程编程技术,分别是:

1、NSThread

优点:NSThread 比其他两个轻量级
缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销

2、NSOperation

优点:不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上。
Cocoa operation 相关的类是 NSOperation ,NSOperationQueue。
NSOperation是个抽象类,使用它必须用它的子类,可以实现它或者使用它定义好的两个子类:NSInvocationOperation和 NSBlockOperation。
创建NSOperation子类的对象,把对象添加到NSOperationQueue队列里执行。

3、GCD

Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。在iOS4.0开始之后才能使用。GCD是一个替代诸如NSThread, NSOperationQueue,NSInvocationOperation等技术的很高效和强大的技术。现在的iOS系统都升级到7了,所以不用担心该技术不能使用。

在iOS开发中使用线程的注意事项:

    1.别将比较耗时的操作放在主线程中

    2.耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种“卡”的坏体验

好了,多线程在ios中的开发概念性的东西就讲这么多,下面我们来模拟一种开发中的场景:

我们在开发中经常会遇到,当你要缓存一组图片,但是这些图片必须要等到你缓冲好了后再来展现在UI上,

可是我们缓存图片的时候用的是SDWebImage框架,缓存的操作是异步进行的

Grand Central Dispatch简称GCD 解决多核并行运算的一种方案     看代码就行:

//Grand CentralDispatch简称GCD技术

// Do any additional setupafter loading the view.

//dispatch_queue_t newDispath=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//dispatch_async(newDispath,^{

// [selfdownloadImage];

// });

//#defineDISPATCH_QUEUE_PRIORITY_HIGH 2

//#defineDISPATCH_QUEUE_PRIORITY_DEFAULT 0

//#defineDISPATCH_QUEUE_PRIORITY_LOW(-2)

//#defineDISPATCH_QUEUE_PRIORITY_BACKGROUNDINT16_MIN

 /*dispatch queue分为下面三种:

   *Serial:又称为private dispatch queues,同时只执行一个任务。Serial queue通常用于同步访问特定的资源或数据。当你创建多个Serialqueue时,虽然它们各自是同步执行的,但Serial queue与Serial queue之间是并发执行的。

* Concurrent: 又称为global dispatch queue,可以并发地执行多个任务,但是执行完成的顺序是随机的。

* Main dispatchqueue它是全局可用的serial queue,它是在应用程序主线程上执行任务的

*/

   //  一般GCD 可以如下操作 

  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{

       // 耗时的操作

      dispatch_async(dispatch_get_main_queue(),^{

           // 更新界面

       });

    });

   [selfexampleDispatch];

 /*

系统给每一个应用程序提供了三个concurrentdispatch queues。

*这三个并发调度队列是全局的,它们只有优先级的不同。

*因为是全局的,我们不需要去创建。我们只需要通过使用函数dispath_get_global_queue去得到队列*/

dispatch_queue_tglobalQ=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0); 

NSLog(@"global:%p",globalQ);

dispatch_queue_t mainQ=dispatch_get_main_queue();

 NSLog(@"mainQ:%p",mainQ);

 /*   *虽然dispatch queue是引用计数的对象,但是以上两个都是全局的队列,不用retain或release。 */

 /*  *dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。

  *这个方法很有用,比如你执行三个下载任务,当三个任务都下载完成后你才通知界面说完成的了。

 */

 timeInt = 0;

[NSTimerscheduledTimerWithTimeInterval:1target:self selector:@selector(checkingTime)

userInfo:nil  ++repeats:YES];

   [selfexampleDispath_group];

/*dispatch_barrier_async的使用

*dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行

     */ 

 [selfexampleDispatch_barrier];

/*dispatch_apply

*执行某个代码片段N次。 */

dispatch_apply(5, globalQ,^(size_t index) {     // 执行5次

});

46.提升UITableView性能的几点建议

(1)学习怎么提升速度,使用Instruments 测量内存与时间消耗,

(2)避免阻塞主线程;使用异步线程让其在后台执行;

(3)重用cells;

(4)缓存下载的图片;使用SDWebImage或者Heneke来管理图片;

(5)使用缓存尽量减少使用富文本标签;

(6)需要缓存计算cell的自定义高度进行计算;

(7)使用不透明视图,极大的提高渲染的速度;

(8)不用过多的使用XIB,如果可能的话可以使用Storboard,当你加载一个XIB,整个上面的内容会被加载都内存中(图片,隐藏的Views),但是storyboad中不会发生他只会实例化当前要的东西

(9)使用懒加载;

47. 线程同步和异步的区别?

同步:一个线程要等待上一个线程执行完之后才能执行当前的线程,生活中的例子(上厕所)。

异步:同时去做两件或者多件事。比如边听歌边看报。

48.堆和栈的区别?

栈区(stack)--由编译器自动分配释放,存放函数的参数值、局部变量的值。先进后出

堆区(heap)--一般由程序员分配释放。先进先出

全局区(静态区)(static)-全局变量和静态变量。程序结束后由系统释放。         

文字常量区--常量字符串存放在这里。程序结束后由系统释放。        

程序代码区—存放函数体的二进制文件。

49.iOS类是否可以多继承?

不可以,可以通过delegate和protocol和类别来实现类似多继承。

50.iOS本地数据存储都有哪几种方式?iOS如何实现复杂对象的存储?

   1. Write写入方式:永久保存在磁盘中。但是只支持NSString、NSData、NSArray、NSDictionary。  

   2.NSKeyedArchiver(归档)采用归档的形式来保存数据,该数据对象需要遵守NSCod-ing协议,并且该对象对应的类必须提供encodeWithCoder:和initWithCoder:方法。

  3.SQLite(FMDB)注意FMDB不是数据库, 而是一个SQLITE管理框架. 

     4.CoreData切记coredata不是数据库, 他的存储核心思想是托管对象, 只是咱们经常用的存储文件为SQLite.还可以用XML, 二进制等方式.

51.iOS的动态性  

1.动态类型。如id类型。实际上静态类型因为其固定性和可预知性而使用得更加广泛。静态类型是强类型,而动态类型属于弱类型。运行时决定接收者。

2. 动态绑定。让代码在运行时判断需要调用什么方法,而不是在编译时。与其他面向对象语言一样,方法调用和代码并没有在编译时连接在一起,而是在消息发送时才进行连接。运行时决定调用哪个方法。

3. 动态载入。让程序在运行时添加代码模块以及其他资源。用户可以根据需要加载一些可执行代码和资源,而不是在启动时就加载所有组件。可执行代码中可以含有和程序运行时整合的新类。

52.什么是安全释放           

在对象dealloc中release之后再把指针置为nil

53.怎样实现一个singleton。

+ (ZMYSingleton *) sharedInstance{

     static LOSingleton *sharedInstance = nil ;

    static dispatch_once_t onceToken; // 锁

   dispatch_once (& onceToken, ^ {        // 最多调用一次      

    sharedInstance = [[ZMYSingleton alloc] init];  

});   

return  sharedInstance;

}

54.RunLoop是什么?

一个RunLoop就是一个事件处理的循环,用来不停的调度工作以及处理输入时间。使用runloop的目的是让你的线程在有工作的时候忙于工作,而没工作的时候处于休眠状态。runloop的设计是为了减少cpu无谓的空转。每个开辟的线程都有一个Runloop, 主线程的Runloop时默认开启的, 咱们手动开辟的子线程Runloop是默认不开启的, 如果需要开启, 需要调用API[[NSRunloop  currentRunloop] run]开启.最常见的需要开启Runloop的是在子线程里面调用计时器(NSTimer), 如果不开启runloop循环方法就不能正常执行.

55.RunTime运行时,是什么?在什么地方用到过?注意事项

在程序运行过程中都会被转化成runtime的C代码执行,例如[target doSomething];会被转化成objc_msgSend(target, @selector(doSomething));。

OC中一切都被设计成了对象,我们都知道一个类被初始化成一个实例,这个实例是一个对象。实际上一个类本质上也是一个对象,在runtime中用结构体表示。

作用环境:

获取列表:有时候会有这样的需求,我们需要知道当前类中每个属性的名字(比如字典转模型,字典的Key和模型对象的属性名字不匹配)。我们可以通过runtime的一系列方法获取类的一些信息(包括属性列表,方法列表,成员变量列表,和遵循的协议列表)。

(2)方法调用:让我们看一下方法调用在运行时的过程(参照前文类在runtime中的表示)

如果用实例对象调用实例方法,会到实例的isa指针指向的对象(也就是类对象)操作。

如果调用的是类方法,就会到类对象的isa指针指向的对象(也就是元类对象)中操作。

首先,在相应操作的对象中的缓存方法列表中找调用的方法,如果找到,转向相应实现并执行。

如果没找到,在相应操作的对象中的方法列表中找调用的方法,如果找到,转向相应实现执行

如果没找到,去父类指针所指向的对象中执行1,2.

以此类推,如果一直到根类还没找到,转向拦截调用。

如果没有重写拦截调用的方法,程序报错。

(3)拦截调用:在方法调用中说到了,如果没有找到方法就会转向拦截调用。

那么什么是拦截调用呢。

拦截调用就是,在找不到调用的方法程序崩溃之前,你有机会通过重写NSObject的四个方法来处理。

+ (BOOL)resolveClassMethod:(SEL)sel;

+ (BOOL)resolveInstanceMethod:(SEL)sel;

//后两个方法需要转发到其他的类处理

- (id)forwardingTargetForSelector:(SEL)aSelector;

- (void)forwardInvocation:(NSInvocation *)anInvocation;

第一个方法是当你调用一个不存在的类方法的时候,会调用这个方法,默认返回NO,你可以加上自己的处理然后返回YES。

第二个方法和第一个方法相似,只不过处理的是实例方法。

第三个方法是将你调用的不存在的方法重定向到一个其他声明了这个方法的类,只需要你返回一个有这个方法的target。

第四个方法是将你调用的不存在的方法打包成NSInvocation传给你。做完你自己的处理后,调用invokeWithTarget:方法让某个target触发这个方法。

(4)动态添加方法:重写了拦截调用的方法并且返回了YES,我们要怎么处理呢?

有一个办法是根据传进来的SEL类型的selector动态添加一个方法。

首先从外部隐式调用一个不存在的方法://隐式调用方法

[target performSelector:@selector(resolveAdd:) withObject:@"test"];

然后,在target对象内部重写拦截调用的方法,动态添加方法。

void runAddMethod(id self, SEL _cmd, NSString *string){

    NSLog(@"add C IMP ", string);

}

+ (BOOL)resolveInstanceMethod:(SEL)sel{

    //给本类动态添加一个方法

    if ([NSStringFromSelector(sel) isEqualToString:@"resolveAdd:"]) {

        class_addMethod(self, sel, (IMP)runAddMethod, "v@:*");

    }

    return YES;

}

其中class_addMethod的四个参数分别是:

Class cls 给哪个类添加方法,本例中是self

SEL name 添加的方法,本例中是重写的拦截调用传进来的selector。

IMP imp 方法的实现,C方法的方法实现可以直接获得。如果是OC方法,可以用+(IMP)instanceMethodForSelector:(SEL)aSelector;获得方法的实现。

"v@:*"方法的签名,代表有一个参数的方法。

(5)关联对象:现在你准备用一个系统的类,但是系统的类并不能满足你的需求,你需要额外添加一个属性。

这种情况的一般解决办法就是继承。

但是,只增加一个属性,就去继承一个类,总是觉得太麻烦类。

这个时候,runtime的关联属性就发挥它的作用了。

//首先定义一个全局变量,用它的地址作为关联对象的key

static char associatedObjectKey;

//设置关联对象

objc_setAssociatedObject(target, &associatedObjectKey, @"添加的字符串属性", OBJC_ASSOCIATION_RETAIN_NONATOMIC); //获取关联对象

NSString *string = objc_getAssociatedObject(target, &associatedObjectKey);

NSLog(@"AssociatedObject = %@", string);

objc_setAssociatedObject的四个参数:

id object给谁设置关联对象。

const void *key关联对象唯一的key,获取时会用到。

id value关联对象。

objc_AssociationPolicy关联策略,有以下几种策略:

如果你熟悉OC,看名字应该知道这几种策略的意思了吧。

objc_getAssociatedObject的两个参数。

id object获取谁的关联对象。

const void *key根据这个唯一的key获取关联对象。

其实,你还可以把添加和获取关联对象的方法写在你需要用到这个功能的类的类别中,方便使用。

1

2

3

4

5

6

7

8

//添加关联对象

- (void)addAssociatedObject:(id)object{

    objc_setAssociatedObject(self, @selector(getAssociatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

//获取关联对象

- (id)getAssociatedObject{

    return objc_getAssociatedObject(self, _cmd);

}

注意:这里面我们把getAssociatedObject方法的地址作为唯一的key,_cmd代表当前调用方法的地址。

(6)方法交换:方法交换,顾名思义,就是将两个方法的实现交换。例如,将A方法和B方法交换,调用A方法的时候,就会执行B方法中的代码,反之亦然。

#import "UIViewController+swizzling.h"

#import @implementation UIViewController (swizzling)

//load方法会在类第一次加载的时候被调用

//调用的时间比较靠前,适合在这个方法里做方法交换

+ (void)load{

    //方法交换应该被保证,在程序中只会执行一次

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        //获得viewController的生命周期方法的selector

        SEL systemSel = @selector(viewWillAppear:);

        //自己实现的将要被交换的方法的selector

        SEL swizzSel = @selector(swiz_viewWillAppear:);

        //两个方法的Method

        Method systemMethod = class_getInstanceMethod([self class], systemSel);

        Method swizzMethod = class_getInstanceMethod([self class], swizzSel);

        //首先动态添加方法,实现是被交换的方法,返回值表示添加成功还是失败

        BOOL isAdd = class_addMethod(self, systemSel, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod));

        if (isAdd) {

            //如果成功,说明类中不存在这个方法的实现

            //将被交换方法的实现替换到这个并不存在的实现

            class_replaceMethod(self, swizzSel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));

        }else{

            //否则,交换两个方法的实现

            method_exchangeImplementations(systemMethod, swizzMethod);

        }

    });

}

- (void)swiz_viewWillAppear:(BOOL)animated{

    //这时候调用自己,看起来像是死循环

    //但是其实自己的实现已经被替换了

    [self swiz_viewWillAppear:animated];

    NSLog(@"swizzle");

}

@end

56.简述应用程序按Home键进入后台时的生命周期,以及从后台回到前台时的生命周期?

进入后台生命周期走:   

- (void)applicationWillResignActive:(UIApplication*)application;  

- (void)applicationDidEnterBackground:(UIApplication*)application;  

回到前台生命周期走:   

- (void)applicationWillEnterForeground:(UIApplication*)application;  

- (void)applicationDidBecomActive:(UIApplication*)application;

57.控制器中的loadView, viewDidLoad,viewWillAppear,viewDidUnload,dealloc、init分别是在什么时候调用的?在自定义ViewController的时候这几个函数里面应该做什么工作?

loadView:没有正在使用nib视图页面,子类将会创建自己的自定义视图层     viewDidLoad:试图被加载后调用

viewWillAppear:试图即将出现的时候调用                                      viewDidUnload:当系统内存吃紧的时候会调用该方法,释放掉当前未在window中显示的试图和对应的控制器

58.描述应用程序的启动顺序。

1、程序入口main函数创建UIApplication实例和UIApplication代理实例

2、在UIApplication代理实例中重写启动方法,设置第一ViewController

3、在第一ViewController中添加控件,实现对应的程序界面。

59.为什么写代理的属性都是assign而不是retain?请举例说明。

防止循环引用,

Teacher *teacher=[[Teacher alloc] init];  

Student *student=[[Student alloc] init];

teacher.delegate=student;          

student.delegate= teacher;

  在teacher中dealloc会release当前的Delegate,就会触发student对象release,继而也会导致student执行dealloc,在student中也会release自己的delegate,产生循环了。

60.UIImage初始化一张图片有几种方法?简述各自的优缺点。

1、从资源读取,这个方法的图片是从缓存里面获取的,先在缓存里面查看是不是有这个图片,没有的话将图片添加进缓存再使用.有的话直接使用缓存里面的.如果这张图片用的次数比较多的话,建议使用这种方式.  缺点是效率低下.UIImage *image = [UIImage imageNamed:@”1.png”];

2、从手机本地读取,比较第一种方式,这个事直接加载图片的,所以建议在图片使用率低的图片时使用这个方法. 

//读取本地图片非resourceNSString *aPath3=[NSString stringWithFormat:@"%@/Documents/%@.jpg",NSHomeDirectory(),@"test"];[UIImage imageWithContentsOfFile:aPath3]

61.这段代码有什么问题吗:

死循环 循环调用,自己调用自己

@implementation Person

- (void)setAge:(int)newAge {

         self.age = newAge;

}

@end

62.用OC写一个冒泡排序,插入排序

(1)冒泡排序思想:两两比较待排序记录的关键字,发现两个记录的次序相反时即进行交换,直到没有反序的记录为止。(从第一个数开始比较)

// 取出第(n+1)个数与前者进行比较
// 比较完第一轮,将最大的数置顶,就好比是气泡一样

NSMutableArray *array = [NSMutableArray arrayWithArray:@[@"3",@"1",@"10",@"5",@"2",@"7",@"12",@"4",@"8"]];

    for (int i= 0; i < array.count; i ++) {

       for (int j = 0; j < array.count  - 1 - i; j++) {

           if([[array objectAtIndex:j] integerValue] > [[array objectAtIndex:j + 1]integerValue]) {

               [arrayexchangeObjectAtIndex:j withObjectAtIndex:j + 1];

           }

       } }

   NSLog(@"%@", array);

(2)插入排序

插入排序实现思想:在已排好的序列中用顺序法查找插入位置,找到后将该位置原来的记录及其后面所有记录顺序后移一个位置,空出该位置来插入新记录。

int i, j;
for (i = 1; i <12; i++) {

if (a[i] < a[i - 1]) // 如果前一个数大于后一个数{

int temp = a[i]; // 保存值
for (j = i - 1; j >= 0 && a[j]> temp; j--) {

a[j + 1] = a[j];}

a[j + 1] = temp;}

}

63.简述你对UIView、UIWindow和CALayer的理解

UIView继承于UIResponder, UIResponder继承于NSObject,UIView可以响应用户事件。

CALayer继承于NSObject,所以CALayer不能响应事件。 UIView构建界面,UIView侧重于对内容的管理,CALayer侧重于对内容的绘制。 UIView是用来显示内容的,可以处理用户事件;CALayer是用来绘制内容的,对内容进行动画处理,依赖与UIView来进行显示,不能处理用户事件。 

64.frame和bounds区别:     

bounds的原点是(0,0)点(就是view本身的坐标系统,默认永远都是0,0点,除非认为setbounds),而frame的原点却是任意的(相对于父视图中的坐标位置)。

65.分析json、xml的区别?json、xml解析方式的底层是如何处理的?

XML是标准通用标记语言(SGML)的子集,非常适合Web传输。XML提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据。       

JSON(JavaScriptObject Notation)一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性。可在不同平台之间进行数据交换。JSON采用兼容性很高的、完全独立 于语言文本格式,同时也具备类似于C语言的习惯(包括C, C++, C#, Java, JavaScript, Perl,Python等)体系的行为。这些特性使JSON成为理想 的数据交换语言。

66.ViewController 的 didReceiveMemoryWarning 是在什么时候被调用的?默认的操作是什么?

didReceiveMemoryWarning在出现内存警告的时候执行该方法,在该方法里面释放掉暂时没使用的可重用的对象。这个方法不能手动调用.

67.面向对象的三大特征,并作简单的介绍

封装、继承、多态。

封装:是把客观事物封装成抽象的类,隐藏内部的实现,对外部提供接口。

继承:可以使用现有类的所有功能,并且在无需重新编写原来的类的情况下对这些功能进行扩展。

多态:不同的对象以自己的方式响应相同的的消息的能力叫做多态,或者说父类指针指向子类对象<如UITableView的,cellForRow方法,返回值类型是UITbaleViewCell,但是你返回的cell可以是你自定义的cell,在比如多个类里面都有同一个方法>

68.简单说一下懒加载

懒加载,又称为延迟加载。通常用法,你有一个UITextField类 型的property,简单定义为userNameTextField,但是你不在初始化方法里为其alloc/init,它就只是一个指针,不会占用内 存。在访问器里判断此property的指针是否为空,若为空,就alloc/init,这时才真正生成这个对象除非这个对象被使用,否则它永远不会真正 生成,也就不会占用内存。

69.分别描述类目(categories)和延展(extensions)是什么?以及两者的区别?继承和类目在实现中有何区别?为什么Category只能为对象添加方法,却不能添加成员变量?

category类目:在不知道源码的情况下为一个类扩展方法,

extension:为一个类声明私有方法和变量。 继承是创建了一个新的类,而类别只是对类的一个扩展,还是之前的类。 类目的作用就是为已知的类添加方法。

70.谈谈你对MVC的理解?为什么要用MVC?在Cocoa中MVC是怎么实现的?你还熟悉其他的OC设计模式或别的设计模式吗?

MVC是Model-VIew-Controller,就是模型-视图-控制器, MVC把软件系统分为三个部分:Model,View,Controller。在cocoa中,你的程序中的每一个object(对象)都将明显地仅属于这三部分中的一个,而完全不属于另外两个。model数据模型,view是对这些数据的显示,viewcontroller就是把model拿到view中显示,起到model和view之间桥梁的作用。MVC可以帮助确保帮助实现程序最大程度的可重用性。各MVC元素彼此独立运作,通过分开这些元素,可以构建可维护,可独立更新的程序组建, 提高代码的重用性.

单例模式,delegate设计模式,target-action设计模式

71、对于语句

NSString* testObject = [[NSData alloc] init];       testObject 在编译时和运行时分别是什么类型的对象?         

编译的时候是NSString类型,运行的时候是NSData类型

72.什么是沙盒(sandbox)?沙盒包含哪些文件,描述每个文件的使用场景。如何获取这些文件的路径?如何获取应用程序包中文件的路径?

iOS应用程序只能在为该改程序创建的文件系统中读取文件,不可以去其它地方访问,此区域被成为沙盒,所以所有的非代码文件都要保存在此,例如图像,图标,声音,映像,属性列表,文本文件等。默认情况下,每个沙盒含有3个文件夹:Documents, Library 和 tmp。 Documents:苹果建议将程序中建立的或在程序中浏览到的文件数据保存在该目录下,iTunes备份和恢复的时候会包括此目录 Library:存储程序的默认设置或其它状态信息;

Library/Caches:存放缓存文件,iTunes不会备份此目录,此目录下文件不会在应用退出删除

tmp:提供一个即时创建临时文件的地方。

iTunes在与iPhone同步时,备份所有的Documents和Library文件。

iPhone在重启时,会丢弃所有的tmp文件。

73.isKindOfClass、isMemberOfClass作用分别是什么?

-(BOOL) isKindOfClass: classObj判断是否是这个类或者是这个类子类的实例

-(BOOL) isMemberOfClass: classObj 判断是否是这个类的实例

74.写出来UITableView的继承关系,写到NSObject(新浪的面试题)

 UITableView –UIScrollView – UIView – UIResponder - NSObject 

75. struct strA{      

int a;     

floats;   

char c; 

} expA;      

printf("%ld",sizeof(expA));    输出结果是几,为什么 ?

该问题涉及编译器的“内存对齐”问题:

现代计算机中内存空间都是按照byte(字节)划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。   

对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台的要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为 32位)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该int数据。显然在读取效率上下降很多。这也是空间和时间的博弈。

通常,我们写程序的时候,不需要考虑对齐问题。编译器会替我们选择适合目标平台的对齐策略。当然,我们也可以通知给编译器传递预编译指令而改变对指定数据的对齐方法。 

但是,正因为我们一般不需要关心这个问题,所以因为编辑器对数据存放做了对齐,而我们不了解的话,常常会对一些问题感到迷惑。最常见的就是struct数据结构的sizeof结果,出乎意料。 

对于结构体来说,按成员中所占字节最大的是float类型,占用4个字节,一共有3个成员,所以总的占用字节为:4 * 3 = 12.  可通过编译器命令来设定:    #progma pack (2)    

76. 网络传输层协议中,基于TCP/IP协议和UDP/IP的连接有什么区别?

TCP:Transmission Control Protocol 传输控制协议TCP是一种面向连接(连接导向)的、可靠的、基于字节流的运输层(Transport layer)通信协议,由IETF的RFC 793说明(specified)。 UDP是User Datagram Protocol的简称,中文名是用户数据包协议,是 OSI 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,IETF RFC 768是UDP的正式规范。   

面向连接:是指通信双方在通信时,要事先建立一条通信线路,其有三个过程:建立连接、使用连接和释放连接。电话系统是一个面向连接的模式,拨号、通话、挂机;TCP协议就是一种面向连接的协议。 

面向无连接:是指通信双方不需要事先建立一条通信线路,而是把每个带有目的地址的包(报文分组)送到线路上,由系统自主选定路线进行传输。邮政系统是一个无连接的模式,天罗地网式的选择路线,天女散花式的传播形式;IP、UDP协议就是一种无连接协议。

77. OSI(Open System Interconnection)开放式系统互联参考模型把网络协议从逻辑上分为了7层,试列举常见的应用层协议。

 注意问的是应用层协议,有些同学直接答了七层模型。 

   在开放系统互连(OSI)模型中的最高层,为应用程序提供服务以保证通信,但不是进行通信的应用程序本身。

Telnet协议是TCP/IP协议族中的一员,是Internet远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。   

FTP文件传输协议是TCP/IP网络上两台计算机传送文件的协议,FTP是在TCP/IP网络和INTERNET上最早使用的协议之一,它属于网络协议组的应用层。

超文本传输协议 (HTTP-Hypertext transfer protocol) 是分布式,协作式,超媒体系统应用之间的通信协议。是万维网(world wide web)交换信息的基础。   

SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式,它帮助每台计算机在发送或中转信件时找到下一个目的地。   

时间协议(TIME protocol)是一个在RFC 868内定义的网络协议。它用作提供机器可读的日期时间资讯。

DNS是域名系统 (Domain Name System) 的缩写,是因特网的一项核心服务,它作为可以将域名和IP地址相互映射的一个分布式数据库。   

SNMP(Simple NetworkManagement Protocol,简单网络管理协议)的前身是简单网关监控协议(SGMP),用来对通信线路进行管理。   

TFTP(Trivial File Transfer Protocol,简单文件传输协议)是TCP/IP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂、开销不大的文件传输服务。端口号为69。

78、iOS中的核心动画?

核心动画作用在CALayer(Coreanimation layer),CALayer从概念上类似于UIView,我们可以将UIView看作一种特殊的CALayer(可以响应事件)。给View加上动画,本质上是对其Layer进行操作,layer包含了各种支持动画的属性,动画则包含了属性变化的值、变化的速度、变化的时间等等,两者结合产生动画的过程。

核心动画和UIView动画的对比:UIView动画可以看成是对核心动画的封装,和UIView动画不同的是,通过核心动画改变layer的状态(比如position),动画执行完毕后实际上是没有改变的(表面上看起来已改变)。

  总体来说核心动画的优点有:

  1)性能强大,使用硬件加速,可以同时向多个图层添加不同的动画效果

  2)接口易用,只需要少量的代码就可以实现复杂的动画效果。

  3)运行在后台线程中,在动画过程中可以响应交互事件(UIView动画默认动画过程中不响应交互事件)。

核心动画类中可以直接使用的类有:

  CABasicAnimation

  CAKeyframeAnimation

  CATransition

  CAAnimationGroup

  CASpringAnimation

3、核心动画类的核心方法

  1.初始化CAAnimation对象

  一般使用animation方法生成实例

1

 + (instancetype)animation;

  如果是CAPropertyAnimation的子类,还可以通过animationWithKeyPath生成实例

1

 + (instancetype)animationWithKeyPath:(nullable NSString *)path;

  2.设置动画的相关属性

  设置动画的执行时间,执行曲线,keyPath的目标值,代理等等

  3.动画的添加和移除

  调用CALayer的addAnimation:forKey:方法将动画添加到CALayer中,这样动画就开始执行了

1

- (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key;

  调用CALayer的removeAnimation方法停止CALayer中的动画

1

2

     - (void)removeAnimationForKey:(NSString *)key;

     - (void)removeAllAnimations;

4、核心动画类的常用属性

  keyPath:可以指定keyPath为CALayer的属性值,并对它的值进行修改,以达到对应的动画效果,需要注意的是部分属性值是不支持动画效果的。

  以下是具有动画效果的keyPath:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

   //CATransform3D Key Paths : (example)transform.rotation.z

     //rotation.x

     //rotation.y

     //rotation.z

     //rotation 旋轉

     //scale.x

     //scale.y

     //scale.z

     //scale 缩放

     //translation.x

     //translation.y

     //translation.z

     //translation 平移

     //CGPoint Key Paths : (example)position.x

     //x

     //y

     //CGRect Key Paths : (example)bounds.size.width

     //origin.x

     //origin.y

     //origin

     //size.width

     //size.height

     //size

     //opacity

     //backgroundColor

     //cornerRadius 

     //borderWidth

     //contents 

     //Shadow Key Path:

     //shadowColor 

     //shadowOffset 

     //shadowOpacity 

    //shadowRadius

duration:动画的持续时间

  repeatCount:动画的重复次数

  timingFunction:动画的时间节奏控制

1

2

3

4

5

6

 timingFunctionName的enum值如下:

     kCAMediaTimingFunctionLinear 匀速

     kCAMediaTimingFunctionEaseIn 慢进

     kCAMediaTimingFunctionEaseOut 慢出

     kCAMediaTimingFunctionEaseInEaseOut 慢进慢出

     kCAMediaTimingFunctionDefault 默认值(慢进慢出)

  fillMode:视图在非Active时的行为

  removedOnCompletion:动画执行完毕后是否从图层上移除,默认为YES(视图会恢复到动画前的状态),可设置为NO(图层保持动画执行后的状态,前提是fillMode设置为kCAFillModeForwards)

  beginTime:动画延迟执行时间(通过CACurrentMediaTime() +your time 设置)

  delegate:代理

1

2

3

代理方法如下:

     - (void)animationDidStart:(CAAnimation *)anim;  //动画开始

     - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag; //动画结束

CABasicAnimation

  CABasicAnimation可以设定keyPath的起点,终点的值,动画会沿着设定点进行移动,CABasicAnimation可以看成是只有两个关键点的特殊的CAKeyFrameAnimation。

下面以改变视图的position为例演示其使用:

1

2

3

4

5

6

7

8

- (void)position {

    CABasicAnimation * ani = [CABasicAnimation animationWithKeyPath:@"position"];

    ani.toValue = [NSValue valueWithCGPoint:self.centerShow.center];

    ani.removedOnCompletion = NO;

    ani.fillMode = kCAFillModeForwards;

    ani.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

    [self.cartCenter.layer addAnimation:ani forKey:@"PostionAni"];

}

79.iOS系统框架分为几层,分别是什么

 1、Core OS是位于iOS系统架构最下面的一层是核心操作系统层,它包括内存管理、文件系统、电源管理以及一些其他的操作系统任务。它可以直接和硬件设备进行交互。作为app开发者不需要与这一层打交道。

 2、Core Services是核心服务层,可以通过它来访问iOS的一些服务。

  3、Media是媒体层,通过它我们可以在应用程序中使用各种媒体文件,进行音频与视频的录制,图形的绘制,以及制作基础的动画效果。 

  4、Cocoa Touch是可触摸层,这一层为我们的应用程序开发提供了各种有用的框架,并且大部分与用户界面有关,本质上来说它负责用户在iOS设备上的触摸交互操作。

80.什么是响应事件者链

    答:当 Application Kit 在应用程序中构造对象时,会为每个窗口建立响应者链。响应者链中的基本对象是 UIWindow 对象及其视图层次。在视图层次中级别较低的视图将比级别更高的视图优先获得处理事件或动作消息的机会。NSWindow中保有一个第一响应者的引用,它通常是当前窗口中处于选择状态的视图,窗口通常把响应消息的机会首先给它。对于事件消息,响应者链通常以发生事件的

口对应的 NSWindow 对象作为结束,虽然其它对象也可以作为下一个响应者被加入到 NSWindow 对象的后面。

81、简述 tableView 的重用机制

     答:表视图的重用机制是一种节约设备内存的机制,尤其是需要显示大量数据时,显得尤其重要。当我们开始创建表视图的一行时,首先定义一个静态的字符串作为单元格的标识符,然后通过该标识符去检测该表视图中有没有闲置的单元格,如果没有,我们就创建一个新的单元格,并且给它指定一个标识符。如果已有闲置的单元格,则直接从表视图中取出来,给单元重新赋上新的值。初学者容易犯的错误时,当我需要自定义一个单元格时,将自定义实例创建在“括号”外,这样单元格中会出现重叠现象,这是初学者常见的错误。此外,如果当前表视图显示 10 个单元格,那么 11 个就可以满足重用的需要。

82、线程和进程的区别

   答:线程是进程的一部分,进程是程序的一部分。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。

线程有自己的堆栈和局部变量,主线程死掉就等于整个进程死掉。所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

83、什么是 MVC,如何理解,什么是MVVM?

   答:M 表示 model,即数据模型,V 表示 view,即视图,C 表示 viewController,即视图控制器,使用 MVC 的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。C存在的目的则是确保M和V的同步。

对于MVVM,Model层是少不了的了,我们得有东西充当DTO(数据传输对象),当然,用字典也是可以的,编程么,要灵活一些。Model层是比较薄的一层,如果学过Java的小伙伴的话,对JavaBean应该不陌生吧。

ViewModel层,就是View和Model层的粘合剂,他是一个放置用户输入验证逻辑,视图显示逻辑,发起网络请求和其他各种各样的代码的极好的地方。说白了,就是把原来ViewController层的业务逻辑和页面逻辑等剥离出来放到ViewModel层。ViewModel:存放各种业务逻辑和网络请求

View层,就是ViewController层,他的任务就是从ViewModel层获取数据,然后显示。ViewController:存放ViewController类资源文件,也就是View层

84、如何发起一个 http 同步、异步网络请求答:

(1)同步请求

// 构造 url
NSURL *url = [NSURLURLWithString:@"http://www.taobao.com"];// 创建一个请求对象
NSURLRequest *request = [NSURLRequestrequestWithURL:url];NSURLResponse *response;
// 发送同步请求,请求成功后返回数据
NSData *resultData = [NSURLConnection

sendSynchronousRequest:request

returningResponse:&responseNSString*datastring = [[NSString alloc] initWithData:resultData

encoding:NSUTF8StringEncoding];self.resultText.text= datastring;

error:nil];

(2)异步请求
NSURL *url = [NSURLURLWithString:request_url];
NSURLRequest *request = [NSURLRequestrequestWithURL:url];
// 发送异步请求
[NSURLConnectionconnectionWithRequest:request

delegate:self];
// 接受加载的数据


  • (void)connection:(NSURLConnection

didReceiveData:(NSData *)data{[_data appendData:data];

}

// 数据加载完以后调用

*)connection

-(void)connectionDidFinishLoading:(NSURLConnection*)connection {

NSString *datastring =[[NSString alloc] initWithData:_data

encoding:NSUTF8StringEncoding];self.resultText.text= datastring;

}大家详见课件。

85、多线程的基本概念,在 cocoa 实现多线程提供了那些解决方案

答:NSThread、block 语句等等详细见课件。

86、网络数据传输一般有几种格式,他们有哪些区别,你又用的哪些框架进行解析的

答:JSON、XML;      

JSON:JSON(JavaScript ObjectNotation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。

1、对象是一个无序的“‘名称/值’对”集合。一个对象以“{”开始,以“}”结束。每个“名称”后跟一个“:”,“‘名称/值’对”之间使用“,”分隔。

2、数组是值(value)的有序集合。一个数组以“[”开始,“]”结束。值之间使用“,”分隔。

XML:可扩展标记语言(extensible Markup Language,简称:XML),是一种标记语言。XML 设计用来传送及携带数据信息,不用来表现或展示数据,HTML语言则用来表现数据,此外,XML 天生有很好的扩展性,JSON当然也有。不过JSON 在 Javascript 主场作战,可以存储 Javascript 复合对象,有着 xml 不可比拟的优势。

87、Dom 和 Sax 解析的优缺点

   答:DOM即Document Object Model,中文叫文档对象模型。
SAX即Simple API for XML的简称,中文叫XML简单应用程序接口。
Dom解析(常见框架是 GDataXMLNode)的优点是解析 XML 变得简单,但是

耗费内存资源,因为它是一次性将 XML 转换成 Dom 对象。Sax 解析 XML 较为复杂,但是解析的速度快、而且占用内存较小(NSXMLParser)。

88、CALayer 的基本概念,它与 UIView 又是什么关系

    答:通常会将 UIView 理解为能够在屏幕上直接制图的对象,实际上从技术的角度而言,视图自身并不在窗口上绘图,它只知道如何在 CALayer 对象上绘图。我们在创建一个 UIView 实例时,该对象会自动创建一个层(自己创建的这个层称为隐式层)。视图在绘图时,将内容画在自己的层上。当视图在层上完成绘图后,系统会将层拷贝到屏幕上。如果我们创建一个 CALayer 层的实例,我们称之为显示层,层其实就是一副位图。(层的委托对象是UIView)

89、cocoa touch 框架

   iPhone OS 应用程序的基础 Cocoa Touch 框架重用了许多 Mac 系统的成熟模式,但是它更多地专注于触摸的接口和优化。
UIKit 为您提供了在 iPhone OS 上实现图形,事件驱动程序的基本工具,其建立在和 Mac OS X 中一样的Foundation 框架上,包括文件处理,网络,字符串操作等。

    Cocoa Touch 具有和 iPhone 用户接口一致的特殊设计。有了 UIKit,您可以使用 iPhone OS 上的独特的图形接口控件,按钮,以及全屏视图的功能,您还可以使用加速仪和多点触摸手势来控制您的应用。

    各色俱全的框架除了 UIKit 外,Cocoa Touch 包含了创建世界一流 iPhone 应用程序需要的所有框架,从三维图形,到专业音效,
甚至提供设备访问 API以控制摄像头,或通过 GPS 获知当前位置。
Cocoa Touch 既包含只需要几行代码就可以完成全部任务的强大的Objective-C框架,也在需要时提供基础的 C 语言 API 来直接访问系统。这些框架包括:Core Animation:通过 Core Animation,您就可以通过一个基于组合独立图层的简单的编程模型来创建丰富的用户体验。

Core Audio:Core Audio 是播放,处理和录制音频的专业技术,能够轻松为您的应用程序添加强大的音频功能。

Core Data:提供了一个面向对象的数据管理解决方案,它易于使用和理解,甚至可处理任何应用或大或小的数据模型。

功能列表:框架分类
下面是 Cocoa Touch 中一小部分可用的框架:
音频和视频:Core Audio ,OpenAL ,Media Library ,AV Foundation数据管理 :Core Data ,SQLite
图形和动画 :Core Animation,OpenGL ES ,Quartz 2D网络:Bonjour ,WebKit ,BSD Sockets
用户应用:Address Book ,Core Location ,Map Kit ,Store Kit

90、将变量保存到本地,列举两个简单的方法

    答:使用 writeToFile 方法,将字符串、字典、数组直接保存。将对象保存到本地,需要实现 NSCoding 协议。

91、 setNeedsLayout、setNeedsDisplay

    答:调用 setNeedsLayout 方法将会调用 UIView 的 layoutSubview,会对视图重新排版;调用 setNeedsDisplay 方法将会调用 UIView 的 drawRect 方法,会对视图重新绘制。(它们都是异步执行的)

92、以下函数主要完成什么事情?

UIApplicationMain(argc, argv,nil, NSStringFromClass([AppDelegate class]))

    答:这个函数被调用的主入口点创建应用程序的对象和应用程序委托和事件循环。注意这个函数虽有返回值,但这个函数在程序没有结束时,永远不会返回。

93、用过什么内存优化管理工具            

答:instruments

94、iOS 后台可以运行哪些类型的程序,如何自定义一个后台运行的任务

    答:audio(音频播放)、location(位置服务)、voip(网络电话),在 plist 文件中 Required backgroud modes 属性中添加需要在后台执行的任务。一般自定义的后台任务只能执行 600s。如果需要自定义一个后台任务超过

600s,见以下代码,详见课件。if (__count% 500 == 0) {

UIApplication*application =[UIApplication sharedApplication];bgTask=[application

beginBackgroundTaskWithExpirationHandler:^(void){

[applicationendBackgroundTask:bgTask];

bgTask =UIBackgroundTaskInvalid;}];}

95、什么是沙盒,文档目录结构的作用

    答:IOS 应用程序只能在为该改程序创建的文件系统中读取文件,不可以去其它地方访问,此区域被成为沙盒。每个应用程序都有自己的存储空间;应用程序不能翻过自己的围墙去访问别的存储空间的内容;应用程序请求的数据都要通过权限检测,假如不符合条件的话,不会被放行。

Documents 目录:您应该将所有的应用程序数据文件写入到这个目录下。这个目录用于存储用户数据或其它应该定期备份的信息。

    AppName.app 目录:这是应用程序的程序包目录,包含应用程序的本身。由于应用程序必须经过签名,所以在运行时不能对这个目录中的内容进行修改,否则可能会使应用程序无法启动。

    Library 目录:这个目录下有两个子目录:Caches 和 Preferences
Preferences 目录包含应用程序的偏好设置文件。我们不应该直接创建偏好设置文件,而是应该使用 NSUserDefaults 类来取得和设置应用程序的偏好。Caches 目录用于存放应用程序专用的支持文件,保存应用程序再次启动过程中需要的信息。

tmp 目录:这个目录用于存放临时文件,保存应用程序再次启动过程中不需要的信息。

获取这些目录路径的方法:1,获取家目录路径的函数:

NSString *homeDir =NSHomeDirectory();


2,获取 Documents 目录路径的方法:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *docDir = [paths objectAtIndex:0];


3,获取 Caches 目录路径的方法:
NSArray *paths =NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES);
NSString *cachesDir = [paths objectAtIndex:0];


4,获取 tmp 目录路径的方法:
NSString *tmpDir = NSTemporaryDirectory();5,获取应用程序程序包中资源文件路径的方法:例如获取程序包中一个图片资源(apple.png)路径的方法:
NSString *imagePath = [[NSBundlemainBundle] pathForResource:@”apple”ofType:@”png”];
UIImage *appleImage =[[UIImage alloc] initWithContentsOfFile:imagePath];代码中的mainBundle 类方法用于返回一个代表应用程序包的对象。

96、http 和 socket 区别

    答:http 是客户端用 http 协议进行请求,发送请求时候需要封装 http 请求头,并绑定请求的数据,服务器一般有 web 服务器配合(当然也非绝对)。 http 请求方式为客户端主动发起请求,服务器才能给响应,一次请求完毕后则断开连接,以节省资源。服务器不能主动给客户端响应(除非采取 http 长连接技术)。iphone主要使用类是 NSURLConnection。

scoket 是客户端跟服务器直接使用socket“套接字”进行连接,并没有规定连接后断开,所以客户端和服务器可以保持连接通道,双方都可以主动发送数据。一般在游戏开发或股票开发这种要求即时性很强并且保持发送数据量比较大的场合使用。主要使用类是 CFSocketRef。 

97、 runtime如何实现weak变量的自动置nil?

     runtime 对注册的类,会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc,假如weak 指向的对象内存地址是a,那么就会以a为键,在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。

98、 能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?

   不能向编译后得到的类中增加实例变量;

   能向运行时创建的类中添加实例变量;

解释下:

   因为编译后的类已经注册在 runtime 中,类结构体中的 objc_ivar_list 实例变量的链表和 instance_size 实例变量的内存大小已经确定,同时runtime会调用 class_setIvarLayout 或class_setWeakIvarLayout 来处理 strong weak 引用。所以不能向存在的类中添加实例变量;

运行时创建的类是可以添加实例变量,调用class_addIvar 函数。但是得在调用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上。

99、runloop和线程有什么关系?

    总的说来,Run loop,正如其名,loop表示某种循环,和run放在一起就表示一直在运行着的循环。实际上,run loop和线程是紧密相连的,可以这样说run loop是为了线程而生,没有线程,它就没有存在的必要。Run loops是线程的基础架构部分, Cocoa 和 CoreFundation 都提供了 run loop 对象方便配置和管理线程的 run loop (以下都以 Cocoa 为例)。每个线程,包括程序的主线程( main thread )都有与之相应的 run loop 对象。

100、runloop 和线程的关系:

(1). 主线程的run loop默认是启动的。

iOS的应用程序里面,程序启动后会有一个如下的main()函数

1

2

3

4

int main(int argc, char * argv[]) {

@autoreleasepool {    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));

}

}

重点是UIApplicationMain()函数,这个方法会为main thread设置一个NSRunLoop对象,这就解释了:为什么我们的应用可以在无人操作的时候休息,需要让它干活的时候又能立马响应。

(2). 对其它线程来说,run loop默认是没有启动的,如果你需要更多的线程交互则可以手动配置和启动,如果线程只是去执行一个长时间的已确定的任务则不需要。

(3). 在任何一个 Cocoa 程序的线程中,都可以通过以下代码来获取到当前线程的

 runloop 。

 

NSRunLoop *runloop = [NSRunLoop currentRunLoop];

101 、runloop的mode作用是什么?

    model 主要是用来指定事件在运行循环中的优先级的,分为:

NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默认,空闲状态

UITrackingRunLoopMode:ScrollView滑动时

UIInitializationRunLoopMode:启动时

NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合

苹果公开提供的 Mode 有两个:

NSDefaultRunLoopMode(kCFRunLoopDefaultMode)

NSRunLoopCommonModes(kCFRunLoopCommonModes)

102 . 以+ scheduledTimerWithTimeInterval…的方式触发的timer,在滑动页面上的列表时,timer会暂定回调,为什么?如何解决?

     RunLoop只能运行在一种mode下,如果要换mode,当前的loop也需要停下重启成新的。利用这个机制,ScrollView滚动过程中NSDefaultRunLoopMode(kCFRunLoopDefaultMode)的mode会切换到UITrackingRunLoopMode来保证ScrollView的流畅滑动:只能在NSDefaultRunLoopMode模式下处理的事件会影响scrllView的滑动。

如果我们把一个NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环中的时候, ScrollView滚动过程中会因为mode的切换,而导致NSTimer将不再被调度。

同时因为mode还是可定制的,所以:

Timer计时会被scrollView的滑动影响的问题可以通过将timer添加到NSRunLoopCommonModes(kCFRunLoopCommonModes)来解决。代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

// 

// http://weibo.com/luohanchenyilong/ (微博@iOS程序犭袁)

// https://github.com/ChenYilong

//将timer添加到NSDefaultRunLoopMode中

[NSTimer scheduledTimerWithTimeInterval:1.0

     target:self

     selector:@selector(timerTick:)

     userInfo:nil

     repeats:YES];

//然后再添加到NSRunLoopCommonModes里

NSTimer *timer = [NSTimer timerWithTimeInterval:1.0

     target:self

     selector:@selector(timerTick:)

     userInfo:nil

     repeats:YES];

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

103. 猜想runloop内部是如何实现的?

一般来讲,一个线程一次只能执行一个任务,执行完成后线程就会退出。如果我们需要一个机制,让线程能随时处理事件但并不退出,通常的代码逻辑 是这样的:

1

2

3

4

5

6

7

function loop() {

    initialize();

    do {

        var message = get_next_message();

        process_message(message);

    } while (message != quit);

}

或使用伪代码来展示下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

// 

// http://weibo.com/luohanchenyilong/ (微博@iOS程序犭袁)

// https://github.com/ChenYilong

int main(int argc, char * argv[]) {

 //程序一直运行状态

 while (AppIsRunning) {

      //睡眠状态,等待唤醒事件

      id whoWakesMe = SleepForWakingUp();

      //得到唤醒事件

      id event = GetEvent(whoWakesMe);

      //开始处理事件

      HandleEvent(event);

 }

 return 0;

}

104. objc使用什么机制管理对象内存?

通过 retainCount 的机制来决定对象是否需要释放。 每次 runloop 的时候,都会检查对象的 retainCount,如果retainCount 为 0,说明该对象没有地方需要继续使用了,可以释放掉了。

105. ARC通过什么方式帮助开发者管理内存?

编译时根据代码上下文,插入 retain/release

106. 不手动指定autoreleasepool的前提下,一个autorealese对象在什么时刻释放?(比如在一个vc的viewDidLoad中创建)

分两种情况:手动干预释放时机、系统自动去释放。

手动干预释放时机--指定autoreleasepool 就是所谓的:当前作用域大括号结束时释放。

系统自动去释放--不手动指定autoreleasepool

Autorelease对象会在当前的 runloop 迭代结束时释放。

如果在一个vc的viewDidLoad中创建一个 Autorelease对象,那么该对象会在 viewDidAppear 方法执行前就被销毁了。

参考链接:《黑幕背后的Autorelease》

107. BAD_ACCESS在什么情况下出现?

访问了野指针,比如对一个已经释放的对象执行了release、访问已经释放对象的成员变量或者发消息。 死循环

108.苹果是如何实现autoreleasepool的?

autoreleasepool以一个队列数组的形式实现,主要通过下列三个函数完成.

objc_autoreleasepoolPush

objc_autoreleasepoolPop

objc_aurorelease

看函数名就可以知道,对autorelease分别执行push,和pop操作。销毁对象时执行release操作。

109. 使用block时什么情况会发生引用循环,如何解决?

一个对象中强引用了block,在block中又使用了该对象,就会发射循环引用。 解决方法是将该对象使用__weak或者__block修饰符修饰之后再在block中使用。

id weak weakSelf = self; 或者 weak __typeof(&*self)weakSelf = self该方法可以设置宏

id __block weakSelf = self;

110. 在block内如何修改block外部变量?

默认情况下,在block中访问的外部变量是复制过去的,即:写操作不对原变量生效。但是你可以加上__block来让其写操作生效,示例代码如下:

1

2

3

4

5

6

__block int a = 0;

void  (^foo)(void) = ^{ 

    a = 1; 

}

f00(); 

//这里,a的值被修改为1

111. 使用系统的某些block api(如UIView的block版本写动画时),是否也考虑引用循环问题?

系统的某些block api中,UIView的block版本写动画时不需要考虑,但也有一些api 需要考虑:

所谓“引用循环”是指双向的强引用,所以那些“单向的强引用”(block 强引用 self )没有问题,比如这些:

1

2

3

4

5

6

[UIView animateWithDuration:duration animations:^{ [self.superview layoutIfNeeded]; }]; 

[[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.someProperty = xyz; }]; 

[[NSNotificationCenter defaultCenter] addObserverForName:@"someNotification"                                                  object:nil 

                           queue:[NSOperationQueue mainQueue]                                              usingBlock:^(NSNotification * notification)                                                  self.someProperty = xyz; }];

这些情况不需要考虑“引用循环”。

但如果你使用一些参数中可能含有 ivar 的系统 api ,如 GCD 、NSNotificationCenter就要小心一点:比如GCD 内部如果引用了 self,而且 GCD 的其他参数是 ivar,则要考虑到循环引用:

1

2

3

4

5

6

7

__weak __typeof__(self) weakSelf = self;

dispatch_group_async(_operationsGroup, _operationsQueue, ^

{

__typeof__(self) strongSelf = weakSelf;

[strongSelf doSomething];

[strongSelf doSomethingElse];

} );

类似的:

1

2

3

4

5

6

7

8

__weak __typeof__(self) weakSelf = self;

  _observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"testKey"                                                               object:nil

                                                                 queue:nil

                                                            usingBlock:^(NSNotification *note) {

      __typeof__(self) strongSelf = weakSelf;

      [strongSelf dismissModalViewControllerAnimated:YES];

  }];

self --> _observer --> block --> self 显然这也是一个循环引用。

112. GCD的队列(dispatch_queue_t)分哪两种类型?

串行队列Serial Dispatch Queue

并行队列Concurrent Dispatch Queue

113. 如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)

使用Dispatch Group追加block到Global Group Queue,这些block如果全部执行完毕,就会执行Main Dispatch Queue中的结束处理的block。

1

2

3

4

5

6

7

8

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(), ^{

        // 合并图片

});

114. dispatch_barrier_async的作用是什么?

在并行队列中,为了保持某些任务的顺序,需要等待一些任务完成后才能继续进行,使用 barrier 来等待之前任务完成,避免数据竞争等问题。 dispatch_barrier_async 函数会等待追加到Concurrent Dispatch Queue并行队列中的操作全部执行完之后,然后再执行 dispatch_barrier_async 函数追加的处理,等 dispatch_barrier_async 追加的处理执行结束之后,Concurrent Dispatch Queue才恢复之前的动作继续执行。

打个比方:比如你们公司周末跟团旅游,高速休息站上,司机说:大家都去上厕所,速战速决,上完厕所就上高速。超大的公共厕所,大家同时去,程序猿很快就结束了,但程序媛就可能会慢一些,即使你第一个回来,司机也不会出发,司机要等待所有人都回来后,才能出发。 dispatch_barrier_async 函数追加的内容就如同 “上完厕所就上高速”这个动作。

115. 苹果为什么要废弃dispatch_get_current_queue?

dispatch_get_current_queue容易造成死锁

116. 以下代码运行结果如何?

1

2

3

4

5

6

7

8

9

- (void)viewDidLoad

{

    [super viewDidLoad];

    NSLog(@"1");

    dispatch_sync(dispatch_get_main_queue(), ^{

        NSLog(@"2");

    });

    NSLog(@"3");

}

只输出:1 。发生主线程锁死。

117. addObserver:forKeyPath:options:context:各个参数的作用分别是什么,observer中需要实现哪个方法才能获得KVO回调?

1

2

3

4

5

6

7

8

// 添加键值观察

/*

1 观察者,负责处理监听事件的对象

2 观察的属性

3 观察的选项

4 上下文

*/

[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"Person Name"];

observer中需要实现一下方法:

1

2

3

4

5

6

7

8

// 所有的 kvo 监听到事件,都会调用此方法

/*

 1. 观察的属性

 2. 观察的对象

 3. change 属性变化字典(新/旧)

 4. 上下文,与监听的时候传递的一致

 */

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;

118. 如何手动触发一个value的KVO

所谓的“手动触发”是区别于“自动触发”:

自动触发是指类似这种场景:在注册 KVO 之前设置一个初始值,注册之后,设置一个不一样的值,就可以触发了。

想知道如何手动触发,必须知道自动触发 KVO 的原理:

键值观察通知依赖于 NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey: 。在一个被观察属性发生改变之前, willChangeValueForKey: 一定会被调用,这就 会记录旧的值。而当改变发生后, didChangeValueForKey: 会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。如果可以手动实现这些调用,就可以实现“手动触发”了。

那么“手动触发”的使用场景是什么?一般我们只在希望能控制“回调的调用时机”时才会这么做。

具体做法如下:

如果这个 value 是 表示时间的 self.now ,那么代码如下:最后两行代码缺一不可。

1

2

3

4

5

6

7

8

9

10

11

12

//  .m文件

//  Created by https://github.com/ChenYilong

//  微博@iOS程序犭袁(http://weibo.com/luohanchenyilong/).

//  手动触发 value 的KVO,最后两行代码缺一不可。

//@property (nonatomic, strong) NSDate *now;

- (void)viewDidLoad

{

    [super viewDidLoad];

    [self willChangeValueForKey:@"now"]; // “手动触发self.now的KVO”,必写。

    [self didChangeValueForKey:@"now"]; // “手动触发self.now的KVO”,必写。

}

但是平时我们一般不会这么干,我们都是等系统去“自动触发”。“自动触发”的实现原理:

比如调用 setNow: 时,系统还会以某种方式在中间插入 wilChangeValueForKey: 、 didChangeValueForKey: 和 observeValueForKeyPath:ofObject:change:context: 的调用。

大家可能以为这是因为 setNow: 是合成方法,有时候我们也能看到人们这么写代码:

1

2

3

4

5

- (void)setNow:(NSDate *)aDate {

    [self willChangeValueForKey:@"now"]; // 没有必要

    _now = aDate;

    [self didChangeValueForKey:@"now"];// 没有必要

}

这是完全没有必要的代码,不要这么做,这样的话,KVO代码会被调用两次。KVO在调用存取方法之前总是调用 willChangeValueForKey: ,之后总是调用 didChangeValueForkey: 。怎么做到的呢?答案是通过 isa 混写(isa-swizzling)。下文《apple用什么方式实现对一个对象的KVO?》会有详述。

119. 若一个类有实例变量 NSString *_foo ,调用setValue:forKey:时,可以以foo还是 _foo 作为key?

都可以。

120. KVC的keyPath中的集合运算符如何使用?

必须用在集合对象上或普通对象的集合属性上

简单集合运算符有@avg, @count@max@min ,@sum,

格式 @"@sum.age"或 @"集合属性.@max.age"

121. KVC和KVO的keyPath一定是属性么?

KVO支持实例变量

122. IBOutlet连出来的视图属性为什么可以被设置成weak?

因为既然有外链那么视图在xib或者storyboard中肯定存在,视图已经对它有一个强引用了。

不过这个回答漏了个重要知识,使用storyboard(xib不行)创建的vc,会有一个叫_topLevelObjectsToKeepAliveFromStoryboard的私有数组强引用所有top level的对象,所以这时即便outlet声明成weak也没关系

123. IB中User Defined Runtime Attributes如何使用?

它能够通过KVC的方式配置一些你在interface builder 中不能配置的属性。当你希望在IB中作尽可能多得事情,这个特性能够帮助你编写更加轻量级的viewcontroller

124. 如何调试BAD_ACCESS错误

1. 重写object的respondsToSelector方法,现实出现EXEC_BAD_ACCESS前访问的最后一个object

2. 通过 Zombie

3. 设置全局断点快速定位问题代码所在行

4. Xcode 7 已经集成了BAD_ACCESS捕获功能:AddressSanitizer。 用法如下:在配置中勾选?Enable Address Sanitizer

125. lldb(gdb)常用的调试命令?

breakpoint 设置断点定位到某一个函数

n 断点指针下一步

po打印对象

更多 lldb(gdb) 调试命令可查看

TheLLDB Debugger ;

苹果官方文档: iOSDebugging Magic 。

126、使用Analyze-静态分析常见的三种泄露

(1)创建了对象没有使用;

(2)创建了对象,且初始化了,但初始化的值一直没有读取过;Value store to ‘X’during its initialization is never.

(3)XXX对象的内存单元有潜在的泄露风险;Potential leak of anobject stored into 'XX'*

126、常用的三方框架

(1) AFNetworking -网络请求

(2)AsyncDisplayKit -异步加载UI

(3)Aspects -Runtime

(4)Charts -图表绘制

(5)CocoaAsyncSocket –Socket

(6) CocoaLumberjack -Logger

(7)DZNEmptyDataSet -空列表UI

(8)fastlane -自动化工具集

(9)FMDB -数据库

(10)FSCalendar -日历

(11)JazzHands -引导页

(12)Kiwi -单元测试

(13)Masonry -Autolayout封装

(14)MGSwipeTableCell -侧滑Cell

(15)MLeaksFinder -内存泄漏

(16) MJExtension -JSON<->Model

(17) MJRefresh -下拉刷新

(18)ReactiveCocoa -响应式编程

(19) SDWebImage -异步加载图片

(20)SVProgressHUD -进度提示

(21)TTTAttributedLabel -图文混排

127、蓝牙连接和数据读写

    蓝牙开发分为中心者模式和管理者模式:1.常用的(其实99.99%)就是使用中心者模式作为开发,就是我们手机作为主机,连接蓝牙外设;2.管理者模式,这个基本用到的比较少,我们手机自己作为外设,自己创建服务和特征,然后有其他的设备连接我们的手机。

在做蓝牙开发之前,最好先了解一些概念:
服务(services):蓝牙外设对外广播的必定会有一个服务,可能也有多个,服务下面包含着一些特征,服务可以理解成一个模块的窗口;
特征(characteristic):存在于服务下面的,一个服务下面也可以存在多个特征,特征可以理解成具体实现功能的窗口,一般特征都会有value,也就是特征值,特征是与外界交互的最小单位;
UUID:可以理解成蓝牙上的唯一标识符(硬件上肯定不是这个意思,但是这样理解便于我们开发),为了区分不同的服务和特征,或者给服务和特征取名字,我们就用UUID来代表服务和特征。

蓝牙连接可以大致分为以下几个步骤
1.建立一个Central Manager实例进行蓝牙管理
2.搜索外围设备
3.连接外围设备
4.获得外围设备的服务
5.获得服务的特征
6.从外围设备读数据
7.给外围设备发送数据
其他:提醒

首先我们先导入系统的BLE的框架
#import

必须遵守2个协议

/* 中心管理者 /
@property (nonatomic, strong) CBCentralManager *cMgr;

/* 连接到的外设 /
@property (nonatomic, strong) CBPeripheral *peripheral;