iOS面试题汇总

一 基础知识

1. static的作用?
  • 1>static修饰的函数是一个内部函数,只能在本文件中调用,其他文件不能调用
  • 2>static修饰的全部变量是一个内部变量,只能在本文件中使用,其他文件不能使用
  • 3>static修饰的局部变量只会初始化一次,并且在程序退出时才会回收内存
2. 堆和栈的区别?
  • 1> 堆空间的内存是动态分配的,一般存放对象,并且需要手动释放内存
  • 2> 栈空间的内存由系统自动分配,一般存放局部变量等,不需要手动管理内存
3. 定义属性时,什么情况使用copy、assign、retain?
  • 1> copy:NSString、Block等类型
  • 2> assign:非OC对象类型, 基本数据类型(两个对象相互引用的时候,一端用retain, 一端用assign)
  • 3> retain:OC对象类型
4. self.跟self->什么区别?
  • 1> self.是调用get方法或者set放
  • 2> self是当前本身,是一个指向当前对象的指针
  • 3> self->是直接访问成员变量
5. id、nil代表什么?
  • 1> id类型的指针可以指向任何OC对象
  • 2> nil代表空值(空指针的值, 0)
6. #import跟#include、@class有什么区别?#import<>跟#import””又什么区别?
  • 1>#import和#include都能完整地包含某个文件的内容,#import能防止同一个文件被包含多次
  • 2>@class仅仅是声明一个类名,并不会包含类的完整声明;@class还能解决循环包含的问题
  • 3>#import <>用来包含系统自带的文件,#import “”用来包含自定义的文件
7. 属性readwrite,readonly,assign,retain,copy,nonatomic各是什么作用,在那种情况下用?
  • 1>readwrite:同时生成get方法和set方法的声明和实现
  • 2>readonly:只生成get方法的声明和实现
  • 3>assign:set方法的实现是直接赋值,用于基本数据类型
  • 4>retain:set方法的实现是release旧值,retain新值,用于OC对象类型
  • 5>copy:set方法的实现是release旧值,copy新值,用于NSString、block等类型
  • 6>nonatomic:非原子性,set方法的实现不加锁(比atomic性能高)
8. 常见的object-c的数据类型有那些,和C的基本数据类型有什么区别?
  • 1>常用OC类型:NSString、NSArray、NSDictionary、NSData、NSNumber等
  • 2>OC对象需要手动管理内存,C的基本数据类型不需要管理内存
9. Difference between shallow copyand deep copy?
  • 1>浅拷贝:指针(地址)拷贝,不会产生新对象
  • 2>深拷贝:内容拷贝,会产生新对象
10. What is advantage ofcategories? What is difference between implementing a category and inheritance?
  • 1>分类可以在不修改原来类模型的基础上拓充方法
  • 2>分类只能扩充方法、不能扩充成员变量;继承可以扩充方法和成员变量
  • 3>继承会产生新的类
11. Difference between categories and extensions?
  • 1>分类是有名称的,类扩展没有名称
  • 2>分类只能扩充方法、不能扩充成员变量;类扩展可以扩充方法和成员变量
  • 3>类扩展一般就写在.m文件中,用来扩充私有的方法和成员变量(属性)
12. What are KVO and KVC?
  • 1>KVC是键值编码,可以通过一个字符串的key(属性名)修改对象的属性值
  • 2>KVO是键值监听,可以监听一个对象属性值的改变
13. what is difference betweenNSNotification and protocol?
  • 1>通过NSNotification可以给多个对象传递数据和消息
  • 2>通过protocol(代理模式)只能给一个对象传递数据和消息
14. What is Singleton?
  • 单例:保证程序运行过程中,永远只有一个对象实例
  • 目的是:全局共享一份资源、节省不必要的内存开销
15. Object-C有私有方法吗?私有变量呢
  • 1>OC没有类似@private的修饰词来修饰方法,只要写在.h文件中,就是公共方法
  • 2>可以使用类扩展(Extension)来增加私有方法和私有变量
16. 关键字const什么含义?
const int a;       
int const a;
const int *a;     
int const *a;
int * const a;    
int const * const a;
  • 1>前两个的作用是一样:a是一个常整型数
  • 2>第三、四个意味着a是一个指向常整型数的指针(整型数是不可修改的,但指针可以)
  • 3>第五个的意思:a是一个指向整型数的常指针(指针指向的整型数是可以修改的,但指针是不可修改的)
  • 4>最后一个意味着:a是一个指向常整型数的常指针(指针指向的整型数是不可修改的,同时指针也是不可修改的)
17. define 和 const常量有什么区别?
  • define在预处理阶段进行替换,const常量在编译阶段使用
  • 宏不做类型检查,仅仅进行替换,const常量有数据类型,会执行类型检查
  • define不能调试,const常量可以调试
  • define定义的常量在替换后运行过程中会不断地占用内存,而const定义的常量存储在数据段只有一份copy,效率更高
  • define可以定义一些简单的函数,const不可以
18. block和weak修饰符的区别?
  • __block不管是ARC还是MRC模式下都可以使用,可以修饰对象,也可以修饰基本数据类型
  • __weak只能在ARC模式下使用,只能修饰对象(NSString),不能修饰基本数据类型
  • block修饰的对象可以在block中被重新赋值,weak修饰的对象不可以
19. 堆和栈的区别
  • 从管理方式来讲
    • 对于栈来讲,是由编译器自动管理,无需我们手工控制;
    • 对于堆来说,释放工作由程序员控制,容易产生内存泄露(memory leak)
  • 从申请大小大小方面讲
    • 栈空间比较小
    • 堆控件比较大
  • 从数据存储方面来讲
    • 栈空间中一般存储基本类型,对象的地址
    • 堆空间一般存放对象本身,block的copy等
20. 写一个便利构造器
-(id)initWithName(NSString *)name age(int)age sex(NSString *)sex
{
self = [super init];

if (self){
    [self setName:name];
    [self setAge:age];
    [self setSex:sex];
}
    return self;
}

+(id)initWithName(NSString *)name age(int)age sex(NSString *)sex
{
  Person *p = [[Person alloc]initWithName:name age:age sex:sex];
    return [p autorelease];
}
21. 截取字符串”20|http://www.baidu.com”中,”|”字符前面和后面的数据,分别输出它们。
NSString *str = @“20|http://www.baidu.com”;
NSArray*arr=[str componentsSeparatedByString:@“|”];
NSLog(@“%@%@”,[arr objectAtIndex:0],[arr objectAtIndex:1]);
22. iOS中是否有多继承?怎么实现多态?
  • 没有
  • 分类和协议
23. 类工厂方法是什么?

答:类工厂方法的实现是为了向客户提供方便,它们将分配和初始化合在一个步骤中,返回被创建的对象,并进行自动释放处理。这些方法的形式是+ (type)className…(其中 className不包括任何前缀)。它们不但可以将分配和初始化合在一起,还可以为初始化过程提供对象的分配信息。类工厂方法的另一个目的是使类(比如NSWorkspace)提供单件实例。虽然init…方法可以确认一个类在每次程序运行过程只存在一个实例,但它需要首先分配一个“生的”实例,然后还必须释放该实例。工厂方法则可以避免为可能没有用的对象盲目分配内存。

24. 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)
  • #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
25. 写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。
  • #defineMIN( A,B) ((A) <= (B) ?(A) :(B))
26. @synthesize和@dynamic关键词
  • 1)@property有两个对应的词,一个是@synthesize,一个是@dynamic。如果@synthesize和@dynamic都没写,那么默认的就是@syntheszie var = _var;
  • 2)@synthesize的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。
  • 3)@dynamic 告诉编译器:属性的setter与getter方法由用户自己实现,不自动生成。(当然对于readonly的属性只需提供getter即可)。假如一个属性被声明为@dynamic var,然后你没有提供@setter方法和@getter方法,编译的时候没问题,但是当程序运行到instance.var = someVar,由于缺setter方法会导致程序崩溃;或者当运行到 someVar = var时,由于缺getter方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。

  • @synthesize

    • 除非开发人员已经做了,否则由编译器自动生成getter/setter方法。
    • 当开发人员自定义存或取方法时,自定义会屏蔽自动生成该方法。
  • @dynamic
    • 告诉编译器,不自动生成getter/setter方法,避免编译期间产生警告。
    • 是由开发人员提供相应的代码:对于只读属性需要提供 setter方法;对于读写属性需要提供 setter 和 getter方法。
27. description方法
  • 使用%@打印一个对象,输出的是什么内容?%@的原理是什么?
    • 答:%@是用来打印对象的, description方法默认返回对象的描述信息(默认实现是返回类名和对象的内存地址).
    • 其实%@的本质是用于打印字符串.
    • 只要利用%@打印某个对象, 系统内部默认就会调用父类的description方法
    • 调用该方法, 该方法会返回一个字符串, 字符串的默认格式 <类的名称: 对象的地址>
  • 重写description方法注意点?
    • 答:如果在description方法中利用%@输出self会造成死循环
    • 建议: 在description方法中尽量不要使用self来获取成员变量
28. 构造方法
  • 什么是构造方法?
    • 在OC中init开头的方法, 我们称之为构造方法
  • 构造方法的用途?
    • 构造方法的用途: 用于初始化一个对象, 让某个对象一创建出来就拥有某些属性和值
  • 如何实现构造方法?
    • 重写init方法, 在init方法中初始化成员变量
  • 如何重写init方法?
    • (1)必须先初始化父类, 再初始化子类
    • (2)必须判断父类是否初始化成功, 只有父类初始化成功才能继续初始化子类
    • (3)返回当前对象的地址
- (instancetype)init
{
    // 初始化父类
    // 只要父类初始化成功 , 就会返回对应的地址, 如果初始化失败, 就会返回nil
    // nil == 0 == 假 == 没有初始化成功
    self = [super init];
    // 判断父类是否初始化成功
    if (self != nil) {
      // 初始化子类
      // 设置属性的值
        _age = 6;
    }
    // 返回地址
    return self;
}
29. 自定义构造方法
  • 什么是自定义构造方法?为什么要自定义构造方法?

    • (1)自定义构造方法就是自定义一个init方法
    • (2)有时候我们需要在创建某个对象的时候,让对象的某些属性就具有值,这时候就需要传入一些参数给对象的属性,为了满足这个需求,就需要自定义构造方法
  • 自定义构造方法的格式?

    • (1)一定是对象方法
    • (2)一定返回id/instancetype
    • (3)方法名称一定以init开头 -(instancetype)initWithAge:(int)age;
  • 自定义构造方法在继承中有一个原则?

    • 自己的事情自己做,属于谁的属性就由谁来进行操作
      父类的属性交给父类的方法来处理,子类的方法处理子类自己独有的属性
  • 自定义构造方法在子类,如何调用的父类构造方法的?

    • 子类在重写自定构造方法时,一般使用super 调用父类的构造方法,先让父类将父类的属性进行初始化
- (instancetype)initWithAge:(int)age andName:(NSString *)name andNo:(int)no
{
    if (self = [super initWithAge:age andName:name]) {
        _no = no;
    }
    return self;
}
30. 类工厂方法
  • 什么是类工厂方法?

    • 用于快速创建对象的类方法, 我们称之为类工厂方法
  • 类工厂方法应用场景?

    • 类工厂方法中主要用于 给对象分配存储空间和初始化这块存储空间
  • 类工厂方法使用规范?

    • 1.一定是类方法 +
    • 2.方法名称以类的名称开头, 首字母小写
    • 3.一定有返回值, 返回值是id/instancetype
    • 4.在类工厂方法实现中,调用本类的构造方法,创建实例对象,并返回实例对象
  • 自定义类工厂方法是苹果的一个规范, 一般情况下, 我们会给一个类提供自定义构造方法和自定义类工厂方法用于创建一个对象。

  • 类工厂方法在继承中的注意点:
    以后但凡自定义类工厂方法, 在类工厂方法中创建对象一定要使用self来创建,一定不要使用类名来创建。

31. @property 后面可以有哪些修饰符?
  • 线程安全的:
    • atomic,nonatomic
  • 访问权限的
    • readonly,readwrite
  • 内存管理(ARC)
    • assign, strong, weak, copy
  • 内存管理(MRC)
    • assign, retain, copy
  • 指定方法名称
    • setter=
    • getter=
32. 什么情况使用 weak 关键字,相比 assign 有什么不同?
  • 在ARC中,出现循环引用的时候,必须要有一端使用weak,比如:自定义View的代理属性
  • 已经自身已经对它进行一次强应用,没有必要在强引用一次,此时也会使用weak,自定义View的子控件属性一般也使用weak;但是也可以使用strong
  • weak当对象销毁的时候,指针会被自动设置为nil,而assign不会* assigin 可以用非OC对象,而weak必须用于OC对象
33. 怎么用 copy 关键字?
  • 对于字符串和block的属性一般使用copy
  • 字符串使用copy是为了外部把字符串内容改了,影响该属性
  • block使用copy是在MRC遗留下来的,在MRC中,方法内部的block是在在栈区的,使用copy可以把它放到堆区.在ACR中对于block使用copy还是strong效果是一样的
34. 这个写法会出什么问题: @property (copy) NSMutableArray *array;
  • 添加,删除,修改数组内的元素的时候,程序会因为找不到对于的方法而崩溃.因为copy就是复制一个不可变NSArray的对象
35. 如何让自己的类用 copy 修饰符?
  • 你是说让我的类也支持copy的功能吗?
  • 如果面试官说是:
    • 遵守NSCopying协议
    • 实现 - (id)copyWithZone:(NSZone *)zone; 方法
  • 如果面试官说否,是属性中如何使用copy
    • 在使用字符串和block的时候一般都使用copy
36. 如何重写带 copy 关键字的 setter?
  • 重写copy的setter方法时候,一定要调用一下传入的对象的copy方法,然后在赋值给该setter的方法对应的成员变量
37. @protocol 和 category 中如何使用 @property
  • 在protocol中使用property只会生成setter和getter方法声明,我们使用属性的目的,是希望遵守我协议的对象的实现该属性
  • category 使用 @property 也是只会生成setter和getter方法的声明,如果我们真的需要给category增加属性的实现,需要借助于运行时的两个函数
    • objc_setAssociatedObject
    • objc_getAssociatedObject
38. ARC下,不显示指定任何属性关键字时,默认的关键字都有哪些?
  • 对应基本数据类型默认关键字是
    • atomic,readwrite,assign
  • 对于普通的OC对象
    • atomic,readwrite,strong
39. 用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
  • 因为父类指针可以指向子类对象,使用copy的目的是为了让本对象的属性不受外界影响,使用copy无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本.
  • 如果我们使用是strong,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性.
40. @synthesize合成实例变量的规则是什么?假如property名为foo,存在一个名为_foo的实例变量,那么还会自动合成新变量么?
  • 如果没有指定成员变量的名称会自动生成一个属性同名的成员变量,如果指定的成员变量的名称,会生成一个指定的名称的成员变量,如果这个成员已经存在了就不再生成了.
    • 如果是 @synthesize foo; 还会生成一个名称为foo的成员变量
    • 如果是 @synthesize foo = _foo; 就不会生成成员变量了.
41. 一个objc对象如何进行内存布局?(考虑有父类的情况)
  • 所有父类的成员变量和自己的成员变量都会存放在该对象所对应的存储空间中.
  • 每一个对象内部都一个isA指针,指向他的类对象,类对象中存放着本对象的对象方法列表和成员变量的列表,属性列表,它内部也有一个isA指针指向元对象(meta class),元对象内部存放的是类方法列表,类对象内部还有一个superclass的指针,指向他的父类对象
  • 根对象就是NSobject
42. OC中SEL是什么,有什么作用?
  • SEL:全称Selector 表示方法的存储位置。
  • 作用
    • 1.配合对象/类来检查对象/类中有没有实现某一个方法
    • 2.配合对象/类来调用某一个SEL方法
    • 3.配合对象将SEL类型作为方法的形参
43. 用变量a给出下面各项的定义。
  • (1) 一个整型数 int a;
  • (2) 一个指向整型数的指针 int *a;
  • (3) 一个指向指针的指针,它指向的指针是指向一个整型数 int **a;
  • (4) 一个有10个整型数的数组 int a[10];
  • (5) 一个有10个指针的数组,该指针是指向一个整型数的 int *a[10];
  • (6) 一个指向有10个整型数数组的指针 int(*a)[10];
  • (7)一个指向数组的指针,该数组有10个指针,每个指针指向一个整型数 int *(*a)[10];
  • (8)一个指向函数的指针,该函数有一个整型参数并返回一个整型数 int(*a)(int);
  • (9)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数 int(*a[10])(int);
  • (10)一个指向函数的指针,该函数有一个整形参数并返回一个指向函数的指针,返回的函数指针指向有一个整型参数且返回一个整型数的函数 int(*(*a)(int))(int);
44. 头文件中的 ifndef/define/endif 干什么用?
  • 防止头文件重复定义。
ifndef/define/endif 的含义:如果未定义 / 那么定义 / 完成假设
一般是用来防止头文件被重复包含,提高编译效率的。
45. const
  • const 有什么用途?

    • (1)可以定义 const 常量
    • (2)const可以修饰函数的参数、返回值,甚至函数的定义体。被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
  • const意味着”只读”。区别如下代码的功能非常重要。

1> const int a;
2>int const a;
3>const int *a;
4>int * const a;
5>int const * a const;
// 前两个作用是一样的,a是一个常整型数。
// 第三个a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。
// 第四个是a是一个指向整型数的常指针(也就是,指针指向的整型数是可以修改的,但指针是不可修改的)。
// 最后一个意味着a是一个指向常整型数的常指针(也就是指针指向的整型数是不可修改的,同时指针也是不可修改的)。
46. extern“C”的作用
  • extern “C”的主要作用就是为了能够正确实现C++代码调用其他C语言代码。
  • 加上extern “C”后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般之包括函数名。
46. 写一个委托的 interface
@protocol MyDelegate;

@interface MyClass: NSObject
{
    id <MyDelegate> delegate;
}

// 委托方法
@protocol MyDelegate

- (void)didJobs:(NSArray *)args;

@end

二 多线程网络

1. 线程和进程的区别?
  • 1> 一个应用程序对应一个进程,一个进程帮助程序占据一块存储空间
  • 2> 要想在进程中执行任务,就必须开启线程,一条线程就代表一个任务
  • 3> 一个进程中允许开启多条线程,也就是同时执行多个任务
2. OC中创建线程的方法是什么?如果指定在主线程中执行代码?如何延时执行代码?
1>创建线程的方法
NSThread
NSOperationQueue和NSOperation
GCD

2>主线程中执行代码
[self performSelectorOnMainThread: withObject: waitUntilDone:];
[self performSelector: onThread:[NSThread mainThread] withObject:waitUntilDone:];
dispatch_async(dispatch_get_main_queue(), ^{

});

3>延时执行

double delayInSeconds = 2.0;
dispatch_time_t popTime =dispatch_time(DISPATCH_TIME_NOW,(int64_t)(delayInSeconds * NSEC_PER_SEC));

dispatch_after(popTime,dispatch_get_main_queue(), ^(void){

});
[self performSelector: withObject: afterDelay:];
[NSTimer scheduledTimerWithTimeInterval: target: selector: userInfo:repeats:];
3. 谈谈你对多线程开发的理解?ios中有几种实现多线程的方法?
  • 1>在一个进程中有多个线程共同存在,每个线程执行各自的任务,相互之间不会干扰,一个线程可以创建或者撤销其他的线程.让一个进程可以同时执行很多任务.这就减少了等待时间,提高了进程的运行效率.但是多线程会耗费资源,并且由于一个线程死掉会造成整个进程死掉.多线程也容易造成安全性问题.
  • 2>
    • Thread
    • GCD
    • NSOperationQueue
    • POSX
    • 异步方法
4. 线程同步和异步的区别?IOS中如何实现多线程的同步?
  • 线程同步是多个线程同时访问同一资源,等待资源访问结束,浪费时间,效率低 ,串行执行任务
  • 线程异步:访问资源时在空闲等待时同时访问其他资源,实现多线程机制,并行执行任务

  • 使用GCD的主队列.使用NSOperationQueue,把最大线程数设为1

5. runloop和线程有什么关系?
  • 每条线程都有唯一的一个RunLoop对象与之对应的
  • 主线程的RunLoop是自动创建并启动
  • 子线程的RunLoop需要手动创建
  • 子线程的RunLoop创建步骤如下:

    • 在子线程中调用[NSRunLoop currentRunLoop]创建RunLoop对象(懒加载,只创建一次)
    • 获得RunLoop对象后要调用run方法来启动一个运行循环

      // 启动RunLoop
      [[NSRunLoop currentRunLoop] run];
    • RunLoop的其他启动方法

    // 第一个参数:指定运行模式
    // 第二个参数:指定RunLoop的过期时间,即到了这个时间后RunLoop就失效了
    [[NSRunLoop currentRunLoop] runMode:kCFRunLoopDefaultMode beforeDate:[NSDate distantFuture]];
6. 猜想runloop内部是如何实现的?
  • 从字面意思看:运行循环、跑圈;
  • 本质:内部就是do-while循环,在这个循环内部不断地处理各种事件(任务),比如:Source、Timer、Observer;
  • 每条线程都有唯一一个RunLoop对象与之对应,主线程的RunLoop默认已经启动,子线程的RunLoop需要手动启动;
  • 每次RunLoop启动时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode,如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入,这样做主要是为了隔离不同Mode中的Source、Timer、Observer,让其互不影响;
7. 不手动指定autoreleasepool的前提下,一个autorealese对象在什么时刻释放?(比如在一个vc的viewDidLoad中创建)
  • 分两种情况:手动干预释放时机、系统自动去释放
    • 手动干预释放时机:指定autoreleasepool就是所谓的:当前作用域大括号结束时就立即释放
    • 系统自动去释放:不手动指定autoreleasepool,Autorelease对象会在当前的 runloop 迭代结束时释放,下面详细说明释放时机
    • RunLoop中的三个状态会处理自动释放池,通过打印代码发现有两个Observer监听到状态值为:1和160(32+128)
      • kCFRunLoopEntry(1) // 第一次进入会创建一个自动释放池
      • kCFRunLoopBeforeWaiting(32) // 进入休眠状态前先销毁自动释放池,再创建一个新的自动释放池
      • kCFRunLoopExit(128) // 退出RunLoop时销毁最后一次创建的自动释放池
8. 苹果是如何实现autoreleasepool的?
  • autoreleasepool以一个队列数组的形式实现,主要通过下列三个函数完成
 objc_autoreleasepoolPush
 objc_autoreleasepoolPop
 objc_aurorelease
  • 看函数名就可以知道,对autorelease分别执行push,和pop操作。销毁对象时执行release操作
9. GCD的队列(dispatch_queue_t)分哪两种类型?背后的线程模型是什么样的?
  • 串行队列
  • 并行队列
  • dispatch_global_queue();是全局并发队列
  • dispatch_main_queue();是一种特殊串行队列
  • 背后的线程模型:自定义队列 dispatch_queue_t queue; 可以自定义是并行:DISPATCH_QUEUE_CONCURRENT 或者 串行DISPATCH_QUEUE_SERIAL
10. HTTP协议中POST方法和GET方法有那些区别?
  • GET用于向服务器请求数据,POST用于提交数据
  • GET请求,请求参数拼接形式暴露在地址栏,而POST请求参数则放在请求体里面,因此GET请求不适合用于验证密码等操作
  • GET请求的URL有长度限制,POST请求不会有长度限制
11. 在使用GCD以及block时要注意些什么?它们两是一回事儿么?block在ARC中和传统的MRC中的行为和用法有没有什么区别,需要注意些什么?
  • Block的使用注意:
    • 1> block的内存管理
    • 2> 防止循环retian
  • 非ARC(MRC):__block
  • ARC:__weak__unsafe_unretained
12. 用过Core Data 或者 SQLite吗?读写是分线程的吗?遇到过死锁没?如何解决的?
  • 用过SQLite,使用FMDB框架
  • 丢给FMDatabaseQueue 或者 添加互斥锁(NSLock/@synchronized(锁对象))
13. 多线程有哪些?主线程和次线程有什么区别?怎么通信?
  • NSThread GCD NSOperation
  • 主线程不需要创建就存在,子线程需要创建,初始时候不存在
  • 更新UI必须在主线程 而进行延时操作一般都在子线程
14. runtime 如何实现 weak 属性
  • runtime 对注册的类会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象地址作为 key,当此对象的引用计数为0的时候会 dealloc, 进而在这个 weak 表中找到此对象地址为键的所有 weak 对象,从而设置为 nil
15. 解析XML文件有哪几种方式?
  • 答:以 DOM 方式解析 XML 文件;以 SAX 方式解析 XML 文件;

三 内存管理

1. 对象是什么时候被释放的?
  • 每个对象都有一个引用计数器,每个新对象的计数器是1,当对象的计数器减为0时,就会被销毁
2. Objective-C如何对内存管理的,说说你的看法和解决方法?
  • 1>每个对象都有一个引用计数器,每个新对象的计数器是1,当对象的计数器减为0时,就会被销毁
  • 2>通过retain可以让对象的计数器+1、release可以让对象的计数器-1
  • 3>还可以通过autorelease pool管理内存
  • 4>如果用ARC,编译器会自动生成管理内存的代码
3. 内存管理的几条原则时什么?按照默认法则.哪些方法生成的对象需要手动释放?在和property结合的时候怎样有效的避免内存泄露?
  • 1>只要调用了alloc、copy、new方法产生了一个新对象,都必须在最后调用一次release或者autorelease
  • 2>只要调用了retain,都必须在最后调用一次release或者autorelease
  • 3>@property如果用了copy或者retian,就需要对不再使用的属性做一次release操作
  • 4>如果用了ARC,另外讨论
4. 什么是安全释放?
  • 先释放. 再将对象的指针置为nil,
5. block在ARC中和MRC中的用法有什么区别,需要注意什么
  • 1.对于没有引用外部变量的Block,无论在ARC还是非ARC下,类型都是NSGlobalBlock,这种类型的block可以理解成一种全局的block,不需要考虑作用域问题。同时,对他进行Copy或者Retain操作也是无效的
  • 2.应注意避免循环引用
6. 自动释放池是什么,工作原理
  • 自动释放池(Autorelease pool)
  • 当您向一个对象发送一个autorelease消息时,Cocoa就会将该对象的一个引用放入到最新的自动释放池。它仍然是个正当的对象,因此自动释放池定义的作用域内的其它对象可以向它发送消息。当程序执行到作用域结束的位置时,自动释放池就会被释放,池中的所有对象也就被释放。

    • 1.ojc-c是通过一种”referring counting”(引用计数)的方式来管理内存的,对象在开始分配内存(alloc)的时候引用计数为一,以后每当碰到有copy,retain的时候引用计数都会加一,每当碰到release和autorelease的时候引用计数就会减一,如果此对象的计数变为了0,就会被系统销毁.
    • 2.NSAutoreleasePool就是用来做引用计数的管理工作的,这个东西一般不用你管的.
    • 3.autorelease和release没什么区别,只是引用计数减一的时机不同而已,autorelease会在对象的使用真正结束的时候才做引用计数减一.

四 数据存储

1. ios本地数据存储都有哪几种方式?
  • plist(属性列表)
  • 数据库(SQLite)
  • 归档和反归档
  • 文件读写
  • coreData
2. 沙盒目录结构是怎样的?各自用于那些场景?
  • Application:存放程序源文件,上架前经过数字签名,上架后不可修改
  • Documents:常用目录,iCloud备份目录,存放数据
  • Library
    • Caches:存放体积大又不需要备份的数据
    • Preference:设置目录,iCloud会备份设置信息
  • tmp:存放临时文件,不会被备份,而且这个文件下的数据有可能随时被清除的可能

五 UI控件

1. Difference between frame and bounds?
  • 1>frame以父控件的左上角为坐标原点
  • 2>bounds以控件本身的左上角为坐标原点
2. pushViewController和presentViewController有什么区别
  • 两者都是在多个试图控制器间跳转的函数
  • presentViewController提供的是一个模态视图控制器(modal)
  • pushViewController提供一个栈控制器数组,push/pop
3. 如何渲染UILabel的文字?
  • 通过NSAttributedString/NSMutableAttributedString(富文本)
4. 触摸事件的传递
  • 触摸事件的传递是从父控件传递到子控件
  • 如果父控件不能接收触摸事件,那么子控件就不可能接收到触摸事件
  • 不能接受触摸事件的四种情况

    • 不接收用户交互,即:userInteractionEnabled = NO
    • 隐藏,即:hidden = YES
    • 透明,即:alpha <= 0.01
    • 未启用,即:enabled = NO
  • 提示:UIImageView的userInteractionEnabled默认就是NO,因此UIImageView以及它的子控件默认是不能接收触摸事件的

  • 如何找到最合适处理事件的控件:
    • 首先,判断自己能否接收触摸事件
      • 可以通过重写hitTest:withEvent:方法验证
    • 其次,判断触摸点是否在自己身上
      • 对应方法pointInside:withEvent:
    • 从后往前(先遍历最后添加的子控件)遍历子控件,重复前面的两个步骤
    • 如果没有符合条件的子控件,那么就自己处理
5. 常用的设计模式
  • 单例模式
  • 组合模式
  • 观察者模式
  • 代理模式
  • 享元模式
  • 工厂方法模式
  • 抽象工厂模式
6. Quatrz 2D的绘图功能的三个核心概念是什么并简述其作用
  • 上下文:主要用于描述图形写入哪里;
  • 路径:是在图层上绘制的内容;
  • 状态:用于保存配置变换的值、填充和轮廓, alpha 值等。
7. 使用AVAudioPlayer类调用哪个框架、使用步骤?
  • AVFoundation.framework
  • 步骤:配置 AVAudioPlayer 对象;
  • 实现 AVAudioPlayer 类的委托方法;
  • 控制 AVAudioPlayer 类的对象;
  • 监控音量水平;
  • 回放进度和拖拽播放。

六 应用程序

1. ViewController 的loadView、viewDidLoad、viewDidUnload分别是什么时候调用的,在自定义ViewCointroller时在这几个函数中应该做什么工作?
  • 1> loadView
    • 当第一次使用控制器的view时,会调用loadView方法创建view
    • 一般在这里自定义view
  • 2> viewDidLoad
    • 当控制器的view创建完毕时会调用,也就是在loadView后调用
    • 一般在这里添加子控件、初始化数据
  • 3> viewDidUnload
    • 当控制器的view因为内存警告被销毁时调用
    • 一般在这里回收跟界面相关的资源(界面都会销毁了,跟界面相关的资源肯定不要了)
2. 怎么理解MVC,在Cocoa中MVC是怎么实现的?
  • 1> M:Model,模型,封装数据
  • 2> V:View,视图界面,负责展示数据
  • 3> C:Controller,控制器,负责提供数据(Model)给界面(View)
3. +(void)load; +(void)initialize;有什么用处?
  • +(void)load;
    • 当类对象被引入项目时, runtime 会向每一个类对象发送 load 消息
    • load方法会在每一个类甚至分类被引入时仅调用一次,调用的顺序:父类优先于子类, 子类优先于分类
    • 由于load 方法会在类被import时调用一次,而这时往往是改变类的行为的最佳时机,在这里可以使用例如method swizlling来修改原有的方法
    • load 方法不会被类自动继承
  • +(void)initialize;
    • 也是在第一次使用这个类的时候会调用这个方法,也就是说initialize也是懒加载
  • 总结:
    • 在Objective-C中,runtime会自动调用每个类的这两个方法
    • +load会在类初始加载时调用
    • +initialize会在第一次调用类的类方法或实例方法之前被调用
    • 这两个方法是可选的,且只有在实现了它们时才会被调用
    • 两者的共同点:两个方法都只会被调用一次
4. 如何制作一个静态库/动态库?他们的区别是什么?
  • Xcode6支持制作静态库/动态库 framework
  • 无论是动态库还是静态库都是区分真机和模拟器的
  • 静态库编译静态库文件装入程序空间,动态库是文件动态装入内存
  • 动态库执行到相关函数才会被调用,节省空间
  • 苹果一般不允许第三方动态库,APP容易被拒
5. KVO内部实现原理
  • 1> KVO是基于runtime机制实现的
  • 2> 当某个类的对象第一次被观察时, 系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。
    • 派生类在被重写的 setter 方法实现真正的通知机制(Person→ NSKVONotifying_Person)
6. info.plist 和Prefix.pch的用处
  • info.plist 项目的配置属性
  • Prefix.pch
    • 1.存放一些全局的宏(整个项目中都用得上的宏)
    • 2.用来包含一些全部的头文件(整个项目中都用得上的头文件)
    • 3.能自动打开或者关闭日志输出功能
7. 请详述viewController的生命周期
  • ViewController的生命周期中各方法执行流程如下:
    • init—>loadView—>viewDidLoad—>viewWillApper—>viewDidApper—>viewWillDisapper—>viewDidDisapper—>viewWillUnload->viewDidUnload—>dealloc
8. IOS中界面间传值的方式有哪些?
  • 属性,代理,block,单例,通知

七 Foundation

1. Foundation对象与Core Foundation对象有什么区别
  • Foundation框架是使用OC实现的,Core Foundation是使用C实现的
  • Foundation对象 和 Core Foundation对象间的转换:俗称桥接

    • ARC环境桥接关键字:

      // 可用于Foundation对象 和 Core Foundation对象间的转换
      __bridge
      
      // 用于Foundation对象 转成 Core Foundation对象
      __bridge_retained
      
      // Core Foundation对象 转成 Foundation对象
      __bridge_transfer
    • Foundation对象 转成 Core Foundation对象

      • 使用__bridge桥接

        • 如果使用__bridge桥接,它仅仅是将strOC的地址给了strC, 并没有转移对象的所有权,也就是说, 如果使用__bridge桥接, 那么如果strOC释放了,strC也不能用了
        • 注意:在ARC条件下,如果是使用__bridge桥接,那么strC可以不用主动释放, 因为ARC会自动管理strOC和strC
          NSString *strOC1 = [NSString stringWithFormat:@"abcdefg"];
          CFStringRef strC1 = (__bridge CFStringRef)strOC1;
          NSLog(@"%@ %@", strOC1, strC1);
      • 使用__bridge_retained桥接

        • 如果使用__bridge_retained桥接,它会将对象的所有权转移给strC, 也就是说, 即便strOC被释放了, strC也可以使用
        • 注意:在ARC条件下,如果是使用__bridge_retained桥接,那么strC必须自己手动释放,因为桥接的时候已经将对象的所有权转移给了strC,而C语言的东西不是不归ARC管理的
          NSString *strOC2 = [NSString stringWithFormat:@"abcdefg"];
          //    CFStringRef strC2 = (__bridge_retained CFStringRef)strOC2;
          CFStringRef strC2 = CFBridgingRetain(strOC2); // 这一句, 就等同于上一句
          CFRelease(strC2);  
    • Core Foundation对象 转成 Foundation对象

      • 使用__bridge桥接

        • 如果使用__bridge桥接,它仅仅是将strC的地址给了strOC, 并没有转移对象的所有权
        • 也就是说如果使用__bridge桥接,那么如果strC释放了,strOC也不能用了
          CFStringRef strC3 = CFStringCreateWithCString(CFAllocatorGetDefault(), "12345678", kCFStringEncodingASCII);
          NSString *strOC3 = (__bridge NSString *)strC3;
          CFRelease(strC3);  
      • 使用__bridge_transfer桥接

        • 如果使用__bridge_transfer桥接,它会将对象的所有权转移给strOC, 也就是说, 即便strC被释放了, strOC也可以使用
        • 如果使用__bridge_transfer桥接, 他会自动释放strC, 也就是以后我们不用手动释放strC
          CFStringRef strC4 = CFStringCreateWithCString(CFAllocatorGetDefault(), "12345678", kCFStringEncodingASCII);
          //     NSString *strOC = (__bridge_transfer NSString *)strC;
          NSString *strOC4 = CFBridgingRelease(strC4); // 这一句, 就等同于上一句  
    • MRC环境:直接强转

      -(void)bridgeInMRC
      {
      // 将Foundation对象转换为Core Foundation对象,直接强制类型转换即可
      NSString *strOC1 = [NSString stringWithFormat:@"xxxxxx"];
      CFStringRef strC1 = (CFStringRef)strOC1;
      NSLog(@"%@ %@", strOC1, strC1);
      [strOC1 release];
      CFRelease(strC1);
      
      // 将Core Foundation对象转换为Foundation对象,直接强制类型转换即可
      CFStringRef strC2 = CFStringCreateWithCString(CFAllocatorGetDefault(), "12345678", kCFStringEncodingASCII);
      NSString *strOC2 = (NSString *)strC2;
      NSLog(@"%@ %@", strOC2, strC2);
      [strOC2 release];
      CFRelease(strC2);
      }

八 其他

1. What is push notification?
  • 1>本地推送:程序内部弹出通知到用户设备
  • 2>远程推送:由推送服务器推送通知到用户设备
    • 本地通知和远程推送通知的基本目的都是让应用程序能够通知用户某些事情, 而且不需要应用程序在前台运行.二者的区别在于本地通知由本应用负责调用,只能从当前设备上的iOS发出, 而远程通知由远程服务器上的程序发送到APNS,再由APNS把消息推送至设备上的程序
2. 用obj-c写一个冒泡排序
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:10];
    for (int i = 0; i <10; i++) {
        [arr addObject:[NSNumber numberWithInt:arc4random()%1000]];
    }
    NSLog(@"====%@",arr);

    for (int i = 0; i < arr.count -1; i++){
        for(int j = 0 ;j < arr.count - i - 1; j++){
            NSNumber* num1 = [arr objectAtIndex:j];
            NSNumber* num2 = [arr objectAtIndex:j+1];
            if( [num1 compare:num2]==NSOrderedDescending){
                [arr exchangeObjectAtIndex:j withObjectAtIndex:j+1];
            }
        }
    }
    NSLog(@"------%@",arr);
3. SDWebImage具体如何实现
  • 1> 利用NSOperationQueue和NSOperation下载图片, 还使用了GCD的一些函数(解码GIF图片)
  • 2> 利用URL作为key,NSOperation作为value
  • 3> 利用URL作为key,UIImage作为value
4. 代码纠错

这里写图片描述

  • 修改方法有很多种,现给出一种做示例:
typedef NS_ENUM(NSInteger, CYLSex) {
    CYLSexMan,
    CYLSexWoman
};

@interface CYLUser : NSObject<NSCopying>

@property (nonatomic, readonly, copy) NSString *name;
@property (nonatomic, readonly, assign) NSUInteger age;
@property (nonatomic, readonly, assign) CYLSex sex;

- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;
+ (instancetype)userWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;

@end
    1. enum 建议使用 NS_ENUM 和 NS_OPTIONS 宏来定义枚举类型
      //定义一个枚举
      typedef NS_ENUM(NSInteger, CYLSex) {
      CYLSexMan,
      CYLSexWoman
      };
    1. age 属性的类型:应避免使用基本类型,建议使用 Foundation 数据类型,对应关系如下:
      • 同时考虑到 age 的特点,应使用 NSUInteger ,而非 int 。
        int -> NSInteger
        unsigned -> NSUInteger
        float -> CGFloat
        动画时间 -> NSTimeInterval
    1. 如果工程项目非常庞大,需要拆分成不同的模块,可以在类、typedef宏命名的时候使用前缀。
    1. doLogIn方法不应写在该类中
    1. doLogIn 方法命名不规范:添加了多余的动词前缀。
      • 应为 -logIn (注意: Login 是名词, LogIn 是动词,都表示登陆。
        如果方法表示让对象执行一个动作,使用动词打头来命名,注意不要使用 do,does 这种多余的关键字,动词本身的暗示就足够了。
    1. -(id)initUserModelWithUserName: (NSString*)name withAge:(int)age;方法中不要用 with 来连接两个参数: withAge: 应当换为age:,age: 已经足以清晰说明参数的作用,也不建议用 andAge: :通常情况下,即使有类似 withA:withB: 的命名需求,也通常是使用withA:andB: 这种命名,用来表示方法执行了两个相对独立的操作(从设计上来说,这时候也可以拆分成两个独立的方法),它不应该用作阐明有多个参数,比如下面的:
//错误,不要使用"and"来连接参数
- (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes;
//错误,不要使用"and"来阐明有多个参数
- (instancetype)initWithName:(CGFloat)width andAge:(CGFloat)height;
//正确,使用"and"来表示两个相对独立的操作
- (BOOL)openFile:(NSString *)fullPath withApplication:(NSString *)appName andDeactivate:(BOOL)flag;
    1. 由于字符串值可能会改变,所以要把相关属性的“内存管理语义”声明为 copy 。
    1. “性别”(sex)属性的:该类中只给出了一种“初始化方法” (initializer)用于设置“姓名”(Name)和“年龄”(Age)的初始值,那如何对“性别”(Sex)初始化?

      @implementation CYLUser
      
      - (instancetype)initWithName:(NSString *)name
                             age:(NSUInteger)age
                             sex:(CYLSex)sex {
        if(self = [super init]) {
            _name = [name copy];
            _age = age;
            _sex = sex;
        }
        return self;
      }
      
      - (instancetype)initWithName:(NSString *)name
                             age:(NSUInteger)age {
        return [self initWithName:name age:age sex:nil];
      }
      
      @end
        1. 按照接口设计的惯例,如果设计了“初始化方法” (initializer),也应当搭配一个快捷构造方法。而快捷构造方法的返回值,建议为 instancetype,为保持一致性,init 方法和快捷构造方法的返回类型最好都用 instancetype。
        1. 如果基于第一种修改方法:既然该类中已经有一个“初始化方法” (initializer),用于设置“姓名”(Name)、“年龄”(Age)和“性别”(Sex)的初始值: 那么在设计对应 @property 时就应该尽量使用不可变的对象:其三个属性都应该设为“只读”。用初始化方法设置好属性值之后,就不能再改变了。在本例中,仍需声明属性的“内存管理语义”。于是可以把属性的定义改成这样
            @property (nonatomic, readonly, copy) NSString *name;
            @property (nonatomic, readonly, assign) NSUInteger age;
            @property (nonatomic, readonly, assign) CYLSex sex;
          由于是只读属性,所以编译器不会为其创建对应的“设置方法”,即便如此,我们还是要写上这些属性的语义,以此表明初始化方法在设置这些属性值时所用的方式。要是不写明语义的话,该类的调用者就不知道初始化方法里会拷贝这些属性,他们有可能会在调用初始化方法之前自行拷贝属性值。这种操作多余而且低效。
        1. initUserModelWithUserName 如果改为 initWithName 会更加简洁,而且足够清晰。
        1. UserModel 如果改为 User 会更加简洁,而且足够清晰。
        1. UserSex如果改为Sex 会更加简洁,而且足够清晰。
        1. 第二个 @property 中 assign 和 nonatomic 调换位置。 推荐按照下面的格式来定义属性
@property (nonatomic, readwrite, copy) NSString *name;
  • 硬伤部分
1. 在-和(void)之间应该有一个空格
2. enum 中驼峰命名法和下划线命名法混用错误:枚举类型的命名规则和函数的命名规则相同:命名时使用驼峰命名法,勿使用下划线命名法。
3. enum 左括号前加一个空格,或者将左括号换到下一行
4. enum 右括号后加一个空格
5. UserModel :NSObject 应为UserModel : NSObject,也就是:右侧少了一个空格。
6. @interface 与 @property 属性声明中间应当间隔一行。
7. 两个方法定义之间不需要换行,有时为了区分方法的功能也可间隔一行,但示例代码中间隔了两行。
8.  -(id)initUserModelWithUserName: (NSString*)name withAge:(int)age;方法中方法名与参数之间多了空格。而且 - 与 (id) 之间少了空格。
9.  -(id)initUserModelWithUserName: (NSString*)name withAge:(int)age;方法中方法名与参数之间多了空格:(NSString*)name 前多了空格。
10.  -(id)initUserModelWithUserName: (NSString*)name withAge:(int)age; 方法中 (NSString*)name,应为 (NSString *)name,少了空格。
5. ios开发中界面布局三种基本方式及特点
  • 手写UI,xib和storyboard。
  • 手写UI是最早进行UI界面布局的方法,优点是灵活自由,缺点是使代码看起来比较长。xib也是比较早出现的UI布局的方式,优点是不用手写代码,但是每个界面都需要自己xib,也是比较麻烦。而storyboard则是在iOS5以后出现的,是xib的聚合体。
6. 建一个工程用到最基本的两个框架是什么?
  • cocoa Touch框架 Function框架
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值