iOS 核心面试题

来源:互联网 发布:猎流软件怎么样 编辑:程序博客网 时间:2024/05/16 05:26

@property 后面可以有哪些修饰符?

1.读写性修饰符:readwrite | readonly

readwrite:表明这个属性是可读可写的,系统为我们创建这个属性的setter和getter方法。

readonly:表明这个属性只能读不能写,系统只为我们创建一个getter方法,不会创建setter方法

2.setter相关修饰符:assign | retain | copy

setter相关的修饰符表明setter方法应该如何实现

assign:表示直接赋值,用于基本数据类型(NSInteger和CGFloat)和C数据类型(如int, float, double, char等)另外还有id类型,这个修饰符不会牵涉到内存管理。但是如果是对象类型,使用此修饰符则可能会导致内存泄漏或EXC_BAD_ACCESS错误

retain:针对对象类型进行内存管理。如果对基本数据类型使用,则Xcode会直接报错。当给对象类型使用此修饰符时,setter方法会先将旧的对象属性release掉,再对新的对象进行一次赋值并进行一次retain操作

copy:主要用在NSString类型,表示复制内容。

系统默认属性是assign。retain是指针的复制,copy是内容的复制

3.原子性修饰符:atomic | nonatomic

atomic:表示是线程安全的。

nonatomic:表示是非线程安全的,使用此属性性能会提高一些。

系统默认是atomic

4.getter和setter修饰符

@property(getter = getMethodName, setter = setMethodName) Object *obj;

这两个属性修饰符用于设置自定义生成的getter和setter方法名,使用之后将不再使用系统默认的setter和getter方法名。

在@property修饰符中可以出现多个修饰符,分别用逗号分隔,但是,在上述修饰符中,1,2,3组中的属性分别之恩那个出现一个,只有4中可以同时出现。

Xcode4.2(iOS sdk4.3和以下版本)和以前的版本用retain和assign

Xcode4.3(iOS 5和以上版本)或之后有了ARC用strong和weak

assign:用于非指针变量。用于基础数据类型(如NSInteger, CGFloat)和C数据类型(int, float, double, char等), 另外还有id类型。

记住:前面不需要加*的就用assign

retain:用于指针变量。一般用于字符串(NSString, NSMutableString), 数组(NSMutableArray, NSArray),字典对象,视图对象(UIView),控制器对象(UIViewController)等

strong类似于retain,weak类似于assign

最简单的记忆:

使用assign:对基础数据类型(如NSInteger, CGFloat)和C数据类型(int, float, double, char等), 另外还有id类型

使用copy:对NSString类型

使用retain:对其它NSObject和其子类

1、在头文件中用@property声明一个属性名,编译器会自动为我们转换成这个属性名的getter方法和setter方法。

2、在实现文件中使用@synthesize propertyName,编译器先会查找这个属性名的setter方法和getter方法有没有被人为实现,如果已经实现,则不再实现,如果没有,则会帮我们生成一个属性命的setter方法和getter方法。

3、当在实现文件中使用了@synthesize propertyName,编译器还会做一件事情,在类成员变量中查找一个名为_propertyName的成员变量,如果没有,再继续查找名为propertyName的成员变量,如果这两个都没有,编译器会自动为我们生成一个私有的名为_propertyName的成员变量。注意,系统自动创建的都是私有的。

4、当在实现文件中这样写@synthesize propertyName = varName;时,setter和getter方法所对应的是一个名为varName的成员变量,修改和读取的是varName成员变量的值。

5、当我们在实现文件中不写@synthesize propertyName时,在Xcode 4.5之前的版本不会帮我们自动实现setter和getter方法,系统当然也不再会为我们生成对应的成员变量。但是在Xcode 4.5之后可以不用写@synthesize了,就跟3、4一样了。

6、当我们既定义了@synthesize,又在实现文件中人为重写setter和getter方法时,那么@synthesize将不再工作,也就不会为我们创建没有定义的_propertyName成员变量了,这时候如果在setter和getter方法中调用_propertyName将会发生编译错误!



用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?

  使用 copy 的目的是为了让本对象的属性不受外界影响,使用copy无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本.

如果使用 strong .这个属性有可能指向一个可变对象,如果这个可变对象被外部意外的修改了,由于可变对象被改变之后起始地址不会发生变化。而附有strong修饰的属性依然指向这这块内存地址,下次读取的时候就会是被改变以后的对象了.也就是说如果使用 strong 可能会被外部意外的修改。

    @property (nonatomic, copy)   NSString *str_copy;    @property (nonatomic, strong) NSString *str_strong;    @property (nonatomic, assign) NSString *str_assign;            //可变对象    NSMutableString *string = [NSMutableString stringWithString:@"hello"];        self.str_copy = string;    self.str_strong = string;    self.str_assign = string;    [string appendString:@"word!"];        //string 改变后,self.str_copy 值不变,self.str_strong,self.str_assign 都跟着变



@protocol 和 category 中如何使用 @property?

          在protocol中使用property只会生成setter和getter方法声明,我们使用属性的目的,是希望遵守我协议的对象的实现该属性

          category 使用 @property 也是只会生成setter和getter方法的声明,如果我们真的需要给category增加属性的实现,需要借助于运行Objective-C动态运行机制中的两个函数

                   objc_setAssociatedObject

                   objc_getAssociatedObject


@synthesize和@dynamic分别有什么作用?

          @property有两个对应的词,一个是@synthesize,一个是@dynamic。如果@synthesize和@dynamic都没写,那么默认的就是@syntheszie var = var,var为property变量。可以手动修改属性var对应的实例变量。例如:@syntheszie var = var1

          @synthesize的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法, 在Xcode4.4之后的版本可以省略不写.

          @dynamic告诉编译器不要自动生成成员变量的getter和setter方法,而是开发者自己手工生成或者运行时生成.


一个objc对象的isa的指针指向什么?有什么作用?

objc对象的isa指针指向类对象,用于寻找对象的方法。



objc中的类方法和实例方法有什么本质区别和联系?

类方法

          类方法保存在类对象的元类中

          类方法只能通过类对象调用

          类方法中的self是类对象

          类方法中不能访问成员变量

          类方法中不能直接调用对象方法

实例方法

          保存在类对象的对象模型中

          实例方法只能通过实例对象调用

          实例方法中的self是实例对象

          实例方法中可以访问成员变量

          实例方法中可以访问成员变量

          实例方法中可以访问成员变量



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

          当前主线程的 RunLoop 正在以 UITrackingRunLoopMode 的模式运行。 这个时候 RunLoop 只会处理与 UITrackingRunLoopMode “绑定”的源, 比如触摸、滚动等事件;而 NSTimer 是默认“绑定”到 NSRunLoopDefaultMode 上的, 所以 Timer 是事情是不会被 RunLoop 处理的,定时器被暂停了!

          常见的解决方案是把Timer“绑定”到 NSRunLoopCommonModes 模式上:

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

这样这个Timer就可以和当前组中的两种模式 UITrackingRunLoopMode 和 kCFRunLoopDefaultMode 相关联了。 RunLoop在这两种模式下,Timer都可以正常运行了。



BAD_ACCESS在什么情况下出现?

          对一个已经释放的对象执行了release

          执行了死循环


苹果是如何实现autoreleasepool的?

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

          objc_autoreleasepoolPush

          objc_autoreleasepoolPop

          objc_aurorelease

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



如何用GCD同步若干个异步调用?(如何让多个线程同时执行同一块代码?)(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)

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

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

        // 合并图片

});


dispatch_barrier_async的作用是什么?

dispatch_barrier_async函数会等待追加到Concurrent Dispatch Queue并行队列中的操作全部执行完之后,然后再执行dispatch_barrier_async函数追加的处理,等dispatch_barrier_async追加的处理执行结束之后,Concurrent Dispatch Queue才恢复之前的动作继续执行。

打个比方:比如你在看电视每个台都在播放精彩的内容,突然插进来一则广告。于是你就换台,结果发现每个台都在放这个广告,等这个广告播放完之后,各个台才开始恢复原来播放的内容。dispatch_barrier_async函数追加的内容就如这条广告。


什么是method swizzling?

在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的。

每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP有点类似函数指针,指向具体的Method实现。


为什么其他语言里叫函数调用,Object-C里则叫给我对象发消息(或者谈下对runtime的理解)

java中,类和方法在编译期就绑定在一起

在OC中,方法调用是向类发送消息,如(bady cry)在运行时会转换成objc_msgSend(bady,cry),向对象发送消息时根据isa指针找到类,在根据类的调度表查找方法,没找到方法则在父类中查找直至基类,如果始终没有找到返回nil

Objective-C 的 Runtime 铸就了它动态语言的特性,这些深层次的知识虽然平时写代码用的少一些,但是却是每个 Objc 程序员需要了解的。Objc Runtime使得C具有了面向对象能力,在程序运行时创建,检查,修改类、对象和它们的方法。可以使用runtime的一系列方法实现。


TCP和UDP有什么区别?

TCP是面向连接的,建立连接需要经历三次握手,保证数据正确性和数据顺序

UDP是非连接的协议,传送数据受生成速度,传输带宽等限制,可能造成丢包

UDP一台服务端可以同时向多个客户端传输信息

TCP报头体积更大,对系统资源要求更多


+(void)load; +(void)initialize;有什么用处?

当类对象被引入项目时, runtime 会向每一个类对象发送 load 消息. load 方法还是非常的神奇的, 因为它会在每一个类甚至分类被引入时仅调用一次, 调用的顺序是父类优先于子类, 子类优先于分类. 而且 load 方法不会被类自动继承, 每一个类中的 load 方法都不需要像 viewDidLoad 方法一样调用父类的方法. 由于 load 方法会在类被 import 时调用一次, 而这时往往是改变类的行为的最佳时机. 我在 DKNightVersion 中使用 method swizlling 来修改原有的方法时, 就是在分类 load 中实现的.

initialize 方法和 load 方法有一些不同, 它虽然也会在整个 runtime 过程中调用一次, 但是它是在该类的第一个方法执行之前调用, 也就是说 initialize 的调用是惰性的, 它的实现也与我们在平时使用的惰性初始化属性时基本相同. 我在实际的项目中并没有遇到过必须使用这个方法的情况, 在该方法中主要做静态变量的设置并用于确保在实例初始化前某些条件必须满足.

在Objective-C中,runtime会自动调用每个类的两个方法。+load会在类初始加载时调用,+initialize会在第一次调用类的类方法或实例方法之前被调用。这两个方法是可选的,且只有在实现了它们时才会被调用。

共同点:两个方法都只会被调用一次。



使用drawRect有什么影响?(这个可深可浅,你至少得用过。。)

drawRect方法依赖Core Graphics框架来进行自定义的绘制,但这种方法主要的缺点就是它处理touch事件的方式:每次按钮被点击后,都会用setNeddsDisplay进行强制重绘;而且不止一次,每次单点事件触发两次执行。这样的话从性能的角度来说,对CPU和内存来说都是欠佳的。特别是如果在我们的界面上有多个这样的UIButton实例。

这个方法的主要作用是根据传入的 rect 来绘制图像 参见文档. 这个方法的默认实现没有做任何事情, 我们可以在这个方法中使用 Core Graphics 和 UIKit 来绘制视图的内容.

这个方法的调用机制也是非常特别. 当你调用 setNeedsDisplay 方法时, UIKit 将会把当前图层标记为 dirty, 但还是会显示原来的内容, 直到下一次的视图渲染周期, 才会为标记为 dirty 的图层重新建立 Core Graphics 上下文, 然后将内存中的数据恢复出来, 再使用 CGContextRef 进行绘制.


sip是什么?

1> SIP(Session Initiation Protocol),会话发起协议

2> SIP是建立VOIP连接的 IETF 标准,IETF是全球互联网最具权威的技术标准化组织

3> 所谓VOIP,就是网络电话,直接用互联网打电话,不用耗手机话费



在异步线程中下载很多图片,如果失败了,该如何处理?请结合RunLoop来谈谈解决方案.(提示:在异步线程中启动一个RunLoop重新发送网络请求,下载图片)

1> 重新下载图片

2> 下载完毕, 利用RunLoop的输入源回到主线程刷新UIImageVIUew



如果后期需要增加数据库中的字段怎么实现,如果不使用CoreData呢?

编写SQL语句来操作原来表中的字段

1> 增加表字段

ALTER TABLE 表名 ADD COLUMN 字段名 字段类型;

2> 删除表字段

ALTER TABLE 表名 DROP COLUMN 字段名;

3> 修改表字段

ALTER TABLE 表名 RENAME COLUMN 旧字段名 TO 新字段名;



简单描述下客户端的缓存机制?

1. 缓存可以分为:内存数据缓存、数据库缓存、文件缓存

2. 每次想获取数据的时候

1> 先检测内存中有无缓存

2> 再检测本地有无缓存(数据库/文件)

3> 最终发送网络请求

4> 将服务器返回的网络数据进行缓存(内存、数据库、文件), 以便下次读取


利用Socket建立网络连接的步骤

建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket 。

套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。

1。服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。

2。客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。

3。连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求








0 0
原创粉丝点击