iOS开发笔试-100道

作者: 强子ly | 来源:发表于2017-07-15 09:56 被阅读1108次

    1、截取字符串”20 | http://www.baidu.com”中,”|”字符前面和后面的数据,分别输出它们

        NSString *str = @"20|http://www.baidu.com";

        NSArray *arr = [str componentsSeparatedByString:@"|"];

        NSLog(@"%@",arr[0]);

        NSLog(@"%@",arr[1]);

    2、.NSString *obj = [[NSSData alloc]init] ,obj在编译时和运行时分别是什么类型的对象

    编译时是nsstring, 运行时是nsdata的一个实例

    3、viewController的loadView,viewDidLoad,viewDidUnload分别是在什么时候调用的?

    loadView:view准备加载到当前屏幕时,检查当前view不存在,调用loadView加载

    viewDidLoad:loadView加载完成后,调用

    viewDidUnload:view不再显示在当前屏幕时,调用。

    4、属性readwrite,readonly,assign,retain,copy,nonatomic 各是什么作用,在那种情况下用?

    readwrite 是可读可写特性;需要生成getter方法和setter方法时

    readonly 是只读特性 只会生成getter方法 不会生成setter方法 ;不希望属性在类外改变

    assign 是赋值特性,setter方法将传入参数赋值给实例变量;仅设置变量时;

    retain 表示持有特性,setter方法将传入参数先保留,再赋值,传入参数的retaincount会+1;

    copy 表示赋值特性,setter方法将传入对象复制一份;需要完全一份新的变量时。

    nonatomic 非原子操作,决定编译器生成的setter getter是否是原子操作,atomic表示多线程安全,一般使用nonatomic

    5、类别的作用?继承和类别在实现中有何区别?

    category 可以在不获悉,不改变原来代码的情况下往里面添加新的方法,只能添加,不能删除修改,并且如果类别和原来类中的方法产生名称冲突,则类别将覆盖原来的方法,因为类别具有更高的优先级。

    类别主要有3个作用:

    1).将类的实现分散到多个不同文件或多个不同框架中。

    2).创建对私有方法的前向引用。

    3).向对象添加非正式协议。

    继承可以增加,修改或者删除方法,并且可以增加属性。

    6、类别和类扩展的区别。

    category和extensions的不同在于 后者可以添加属性。另外后者添加的方法是必须要实现的。

    extensions可以认为是一个私有的Category。

    7、frame和bounds有什么不同?

    frame指的是:该view在父view坐标系统中的位置和大小。(参照点是父亲的坐标系统)

    bounds指的是:该view在本身坐标系统中 的位置和大小。(参照点是本身坐标系统)

    8、import 跟#include 又什么区别,@class呢, #import<> 跟 #import””又什么区别?

    import是Objective-C导入头文件的关键字,#include是C/C++导入头文件的关键字,使用#import头文件会自动只导入一次,不会重复导入,相当于#include和#pragma once;

    @class告诉编译器某个类的声明,当执行时,才去查看类的实现文件,可以解决头文件的相互包含;#import<>用来包含系统的头文件,#import””用来包含用户头文件。

    9、以下代码运行结果如何?

    - (void)viewDidLoad{

    [super viewDidLoad];

    NSLog(@"1");

    dispatch_sync(dispatch_get_main_queue(), ^{

    NSLog(@"2");

    });

    NSLog(@"3");

    }

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

    10、关于block

    void test1()

    {

    int a = 10;

    void (^block)() = ^{

    NSLog(@"a is %d", a);

    };

    a = 20;

    block(); // 10

    }

    void test2()

    {

    __block int a = 10;

    void (^block)() = ^{

    NSLog(@"a is %d", a);

    };

    a = 20;

    block(); // 20

    }

    void test3()

    {

    static int a = 10;

    void (^block)() = ^{

    NSLog(@"a is %d", a);

    };

    a = 20;

    block(); // 20

    }

    int a = 10;

    void test4()

    {

    void (^block)() = ^{

    NSLog(@"a is %d", a);

    };

    a = 20;

    block();//20

    }

    11、block和weak区别?

    __block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。

    __weak只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。

    block对象可以在block中被重新赋值,weak不可以。

    12、创建一个单例

    +(instancetype)shareSingleton{

    static Singleton *singleton = nil;

    static dispatch_once_t onceToken;  //给单例加了一个线程锁

    dispatch_once(&onceToken, ^{

    singleton = [[Singleton alloc] init];

    });

    return singleton;

    13、写一个标准宏MIN,这个宏输入两个参数并返回较小的一个

    #define kMIN(X,Y) ((X) > (Y)) ? (Y) :(X)

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

    进入后台生命周期走:

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

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

    回到前台生命周期走:

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

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

    15、将字符串“2015-04-10”格式化日期转为NSDate类型

    NSString *timeStr = @"2015-04-10";

    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];

    formatter.dateFormat = @"yyyy-MM-dd";

    formatter.timeZone = [NSTimeZone defaultTimeZone];

    NSDate *date = [formatter dateFromString:timeStr];

    // 2015-04-09 16:00:00 +0000

    NSLog(@"%@", date);

    16、isMemberOfClass 和 isKindOfClass 联系与区别

    联系:两者都能检测一个对象是否是某个类的成员

    区别:

    isKindOfClass:确定一个对象是否是一个类的成员,或者是派生自该类的成员.

    isMemberOfClass:确定一个对象是否是当前类的成员.

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

    17、描述应⽤用程序的启动顺序

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

    重写UIApplication代理实例,设置rootviewcontroller.

    在第一viewController中添加控件,实现对应的程序界面.

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

    self.name = "object"会调用对象的setName()方法,

    name = "object"会直接把object赋值给当前对象的name 属性。

    并且 self.name 这样retainCount会加1,而name就不会。

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

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

    20、我们说的oc是动态运行时语言是什么意思?

    答案:多态。 主要是将数据类型的确定由编译时,推迟到了运行时。

    这个问题其实浅涉及到两个概念,运行时和多态。

    简单来说,运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。

    多态:不同对象以自己的方式响应相同的消息的能力叫做多态。意思就是假设生物类(life)都用有一个相同的方法-eat;

    那人类属于生物,猪也属于生物,都继承了life后,实现各自的eat,但是调用是我们只需调用各自的eat方法。

    也就是不同的对象以自己的方式响应了相同的消息(响应了eat这个选择器)。

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

    当您向一个对象发送一个autorelease消息时,Cocoa就会将该对象的一个引用放入到最新的自动释放.它仍然是个正当的对象,因此自动释放池定义的作用域内的其它对象可以向它发送消息。当程序执行到作用域结束的位置时,自动释放池就会被释放,池中的所有对象也就被释放。

    22、简述内存分区情况

    1).代码区:存放函数二进制代码

    2).数据区:系统运行时申请内存并初始化,系统退出时由系统释放。存放全局变量、静态变量、常量

    3).堆区:通过malloc等函数或new等操作符动态申请得到,需程序员手动申请和释放

    4).栈区:函数模块内申请,函数结束时由系统自动释放。存放局部变量、函数参数

    23、队列和栈有什么区别

    队列和栈是两种不同的数据容器。从”数据结构”的角度看,它们都是线性结构,即数据元素之间的关系相同。

    队列是一种先进先出的数据结构,它在两端进行操作,一端进行入队列操作,一端进行出列队操作。

    栈是一种先进后出的数据结构,它只能在栈顶进行操作,入栈和出栈都在栈顶操作。

    24、Quatrz 2D的绘图功能的三个核心概念是什么并简述其作用。

    答:上下文:主要用于描述图形写入哪里;

    路径:是在图层上绘制的内容;

    状态:用于保存配置变换的值、填充和轮廓, alpha 值等。

    25、什么是沙盒模型?哪些操作是属于私有api范畴?

    某个iphone工程进行文件操作有此工程对应的指定的位置,不能逾越。

    iphone沙箱模型的有四个文件夹documents,tmp,app,Library,永久数据存储一般

    放documents文件夹,得到模拟器的路径的可使用NSHomeDirectory()方法。

    Nsuserdefaults保存的文件在tmp文件夹里。

    26、重写一个NSString类型的,retain方式声明name属性的setter和getter方法

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

    if(_name){

    [_name release];

    }

    _name = [name retain];

    }

    -(NSString *)getterName{

    return [[_name retain]autorelease];

    27、如监测系统键盘的弹出

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShown:) name:UIKeyboardWillShowNotification object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];

    /** 键盘弹出动画 */

    -(void)onKeyboradShow:(NSNotification *)notify

    {

    CGFloat keyboard = [[notify.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey]CGRectValue].size.height;

    CGFloat duration = [[notify.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey]floatValue];

    [UIView animateWithDuration:duration + 0.25 delay:0 usingSpringWithDamping:1 initialSpringVelocity:1 options:UIViewAnimationOptionLayoutSubviews animations:^{

    self.inputView.frame = CGRectMake(0, k_screenHeight - keyboard - 50, k_screenWidth, 50);

    } completion:nil];

    self.isOriginal = NO;

    self.maskView.hidden = NO;

    }

    /** 键盘收起动画 */

    -(void) onKeyboradHide:(NSNotification *) notify

    {

    CGFloat duration = [[notify.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey]floatValue];

    [UIView animateWithDuration:duration delay:0 usingSpringWithDamping:1 initialSpringVelocity:1 options:UIViewAnimationOptionLayoutSubviews animations:^{

    self.inputView.frame = CGRectMake(0, k_screenHeight - 50, k_screenWidth, 50);

    } completion:nil];

    self.isOriginal = NO;

    self.maskView.hidden = YES;

    }

    28、用OC写一个冒泡排序

    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]) {

            [array exchangeObjectAtIndex:j withObjectAtIndex:j + 1];

            }

        }

    }

    NSLog(@"%@",array);

    29、回答person的retainCount值,并解释为什么

    Person * per = [[Person alloc] init]; 此时person 的retainCount的值是1

    self.person = per;

    在self.person 时,如果是assign,person的 retainCount的值不变,仍为1

    若是:retain person的retainCount的值加1,变为2

    若是:copy person的retainCount值不变,仍为1

    30、实用sql语句查询出省名以“湖”开头,右边为“436001”所在的市区

    SELECT *FROM CITYS WHERE PROVINCE_NAME LIKE ‘湖%’ AND POST_CODE = 436001;

    31、打印结果为多少

    int a[5] = {1,2,3,4,5}; 

    int *ptr = (int *)(&a+1);

    NSLog(@"%d,%d",*(a + 1),*(ptr - 1));

    打印结果为: 2,5

    1.*(a+1)=a[0+1]=a[1]=2

    2.&a表示对数组取地址,&a+1表示a[5]后面一个地址,(ptr-1)表示对当前数组元素地址向前移动一位,并取值,故等于a[4]=5

    32、用预处理指令#define声明一个常数,用以表明1年中有多少秒(忽略闰年问题)

    #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL

    33、简述内存分区情况

    1)代码区:存放函数二进制代码

    2)数据区:系统运行时申请内存并初始化,系统退出时由系统释放。存放全局变量、静态变量、常量

    3)堆区:通过malloc等函数或new等操作符动态申请得到,需程序员手动申请和释放

    4)栈区:函数模块内申请,函数结束时由系统自动释放。存放局部变量、函数参数

    34、const char *p;  charconst*p;  char*const p;  const char* const p;四个修饰指针有什么区别

    1)定义了一个指向不可变的字符串的字符指针

    2)和(1)一样

    3)定义了一个指向字符串的指针,该指针值不可改变,即不可改变指向

    4)定义了一个指向不可变的字符串的字符指针,且该指针也不可改变指向

    35、OC中加号方法与减号的区别?

    加号方法是类方法,属于静态方法

    减号方法是实例方法必须由类的实例来调用

    36、ViewController 的 loadView, viewDidLoad,viewDidUnload 分别是在什么时候调用的?在自定义ViewController的时候这几个函数里面应该做什么工作?

    viewDidLoad在view 从nib文件初始化时调用,loadView在controller的view为nil时调用。此方法在编程实现view时调用,view 控制器默认会注册memory warning notification,当view controller的任何view 没有用的时候,viewDidUnload会被调用,在这里实现将retain 的view release,如果是retain的IBOutlet view 属性则不要在这里release,IBOutlet会负责release 。

    37、有哪几种手势通知方法、写清楚方法名

    -(void)touchesBegan:(NSSet*)touchedwithEvent:(UIEvent*)event;

    -(void)touchesMoved:(NSSet*)touched withEvent:(UIEvent*)event;

    -(void)touchesEnded:(NSSet*)touchedwithEvent:(UIEvent*)event;

    -(void)touchesCanceled:(NSSet*)touchedwithEvent:(UIEvent*)event;

    38、id 声明的对象有什么特性?

    id 是个很重要的类型,是个可以指向任何类型的指针或者可以理解为指向任何未知类型的指针。

    39、获取项目根路径,并在其下创建一个名称为userData 的目录

    // 获取根路径

    NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);

    NSString *documentsDirectory = [paths objectAtIndex:];

    // 创建文件系统管理器

    NSFileManager *fileManager = [[NSFileManageralloc] init];

    // 判断userData 目录是否存在

    if(![fileManagerfileExistsAtPath:[NSStringstringWithFormat:@"%@/userData",documentsDirectory]]) {

    // 不存在, 创建一个userData目录

    [fileManagercreateDirectoryAtPath:[NSStringstringWithFormat:@"%@/userData",documentsDirectory]withIntermediateDirectories:falseattributes:nilerror:nil];

    }

    40、tableView 的重用机制

    UITableView 通过重用单元格来达到节省内存的目的: 通过为每个单元格指定一个重用标识符(reuseIdentifier),即指定了单元格的种类,以及当单元格滚出屏幕时,允许恢复单元格以便重用.对于不同种类的单元格使用不同的ID,对于简单的表格,一个标识符就够了.

    41、用变量a 给出下面的定义

    a)    一个整型

    b)    一个指向整型数的指针

    c)    一个指向指针的的指针,它指向的指针是指向一个整型数

    d)    一个有10 个整型数的数组

    e)    一个有10 个指针的数组,该指针是指向一个整型数的

    f)    一个指向有10 个整型数数组的指针

    g)    一个指向函数的指针,该函数有一个整型参数并返回一个整型数

    h)    一个有10 个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数

    a)    int a;

    b)    int *a;

    c)    int **a;

    d)    int a[10]

    e)    int *a[10];

    f)    int (*a)[10];

    g)    int (*a)(int);

    i)    int (*a[10])(int);

    42、用NSLog 函数输出一个浮点类型,结果四舍五入,并保留一位小数

    NSLog(@”%0.1f”,4.4324);

    43、switch 语句 if 语句区别与联系

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

    44、对象可以被copy的条件

    只有实现了NSCopying和NSMutableCopying协议的类的对象才能被拷贝,分为不可变拷贝和可变拷贝,

    NSCopying协议方法为:

    - (id)copyWithZone:(NSZone *)zone

    {

    Person *person = [[[Person class] allocWithZone:zone] init];

    person.age = self.age;

    person.name = self.name;

    return person;

    }

    45、UITableViewCell上有个UILabel,显示NSTimer实现的秒表时间,手指滚动cell过程中,label是否刷新,为什么?

    这是否刷新取决于timer加入到Run Loop中的Mode是什么。Mode主要是用来指定事件在运行循环中的优先级的,分为:

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

    UITrackingRunLoopMode:ScrollView滑动时会切换到该Mode

    UIInitializationRunLoopMode:run loop启动时,会切换到该mode

    NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合

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

    NSDefaultRunLoopMode(kCFRunLoopDefaultMode)

    NSRunLoopCommonModes(kCFRunLoopCommonModes)

    在编程中:如果我们把一个NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环中的时候, ScrollView滚动过程中会因为mode的切换,而导致NSTimer将不再被调度。当我们滚动的时候,也希望不调度,那就应该使用默认模式。但是,如果希望在滚动时,定时器也要回调,那就应该使用common mode。

    46、有a、b、c、d 4个异步请求,如何判断a、b、c、d都完成执行?如果需要a、b、c、d顺序执行,该如何实现?

    对于这四个异步请求,要判断都执行完成最简单的方式就是通过GCD的group来实现:

    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, ^{ /*任务a */ });

    dispatch_group_async(group, queue, ^{ /*任务b */ });

    dispatch_group_async(group, queue, ^{ /*任务c */ });

    dispatch_group_async(group, queue, ^{ /*任务d */ });

    dispatch_group_notify(group,dispatch_get_main_queue(), ^{ // 在a、b、c、d异步执行完成后,会回调这里});

    当然,我们还可以使用非常老套的方法来处理,通过四个变量来标识a、b、c、d四个任务是否完成,然后在runloop中让其等待,当完成时才退出runloop。但是这样做会让后面的代码得不到执行,直到Run loop执行完毕。

    解释:要求顺序执行,那么可以将任务放到串行队列中,自然就是按顺序来异步执行了。

    47、使用block有什么好处?使用NSTimer写出一个使用block显示(在UILabel上)秒表的代码

    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0

    repeats:YES

    callback:^() {

    weakSelf.secondsLabel.text = ...

    }

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

    48、一个view已经初始化完毕,view上面添加了n个button,除用view的tag之外,还可以采用什么办法来找到自己想要的button来修改button的值

    第一种:如果是点击某个按钮后,才会刷新它的值,其它不用修改,那么不用引用任何按钮,直接在回调时,就已经将接收响应的按钮给传过来了,直接通过它修改即可。

    第二种:点击某个按钮后,所有与之同类型的按钮都要修改值,那么可以通过在创建按钮时将按钮存入到数组中,在需要的时候遍历查找。

    49、TCP连接的三次握手

    第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;

    第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包,即SYN+ACK包,此时服务器进入SYN+RECV状态;

    第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次状态。

    50、Scoket连接和HTTP连接的区别:

    HTTP协议是基于TCP连接的,是应用层协议,主要解决如何包装数据。Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。

    HTTP连接:短连接,客户端向服务器发送一次请求,服务器响应后连接断开,节省资源。服务器不能主动给客户端响应(除非采用HTTP长连接技术),iPhone主要使用类NSURLConnection。

    Socket连接:长连接,客户端跟服务器端直接使用Socket进行连接,没有规定连接后断开,因此客户端和服务器段保持连接通道,双方可以主动发送数据,一般多用于游戏.Socket默认连接超时时间是30秒,默认大小是8K(理解为一个数据包大小)。

    51、HTTP协议的特点,关于HTTP请求GET和POST的区别

    GET和POST的区别:HTTP超文本传输协议,是短连接,是客户端主动发送请求,服务器做出响应,服务器响应之后,链接断开。HTTP是一个属于应用层面向对象的协议,HTTP有两类报文:请求报文和响应报文。HTTP请求报文:一个HTTP请求报文由请求行、请求头部、空行和请求数据4部分组成。HTTP响应报文:由三部分组成:状态行、消息报头、响应正文。GET请求:参数在地址后拼接,没有请求数据,不安全(因为所有参数都拼接在地址后面),不适合传输大量数据(长度有限制,为1024个字节)。  GET提交、请求的数据会附在URL之后,即把数据放置在HTTP协议头中。

    以?分割URL和传输数据,多个参数用&连接。如果数据是英文字母或数字,原样发送,

    如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密。POST请求:参数在请求数据区放着,相对GET请求更安全,并且数据大小没有限制。把提交的数据放置在HTTP包的包体中.GET提交的数据会在地址栏显示出来,而POST提交,地址栏不会改变。传输数据的大小:GET提交时,传输数据就会受到URL长度限制,POST由于不是通过URL传值,理论上书不受限。安全性:POST的安全性要比GET的安全性高;通过GET提交数据,用户名和密码将明文出现在URL上,比如登陆界面有可能被浏览器缓存。HTTPS:安全超文本传输协议(Secure Hypertext Transfer Protocol),它是一个安全通信通道,基于HTTP开发,用于客户计算机和服务器之间交换信息,使用安全套结字层(SSI)进行信息交换,即HTTP的安全版。

    52、网络七层协议

    应用层:

    1.用户接口、应用程序;

    2.Application典型设备:网关;

    3.典型协议、标准和应用:TELNET、FTP、HTTP

    表示层:

    1.数据表示、压缩和加密presentation

    2.典型设备:网关

    3.典型协议、标准和应用:ASCLL、PICT、TIFF、JPEG|MPEG

    4.表示层相当于一个东西的表示,表示的一些协议,比如图片、声音和视频MPEG。

    会话层:

    1.会话的建立和结束;

    2.典型设备:网关;

    3.典型协议、标准和应用:RPC、SQL、NFS、X WINDOWS、ASP

    传输层:

    1.主要功能:端到端控制Transport;

    2.典型设备:网关;

    3.典型协议、标准和应用:TCP、UDP、SPX

    网络层:

    1.主要功能:路由、寻址Network;

    2.典型设备:路由器;

    3.典型协议、标准和应用:IP、IPX、APPLETALK、ICMP;

    数据链路层:

    1.主要功能:保证无差错的疏忽链路的data link;

    2.典型设备:交换机、网桥、网卡;

    3.典型协议、标准和应用:802.2、802.3ATM、HDLC、FRAME RELAY;

    物理层:

    1.主要功能:传输比特流Physical;

    2.典型设备:集线器、中继器

    3.典型协议、标准和应用:V.35、EIA/TIA-232.

    53、对NSUserDefaults的理解

    NSUserDefaults:系统提供的一种存储数据的方式,主要用于保存少量的数据,默认存储到library下的Preferences文件夹。

    54、SDWebImage原理

    从内存中(字典)找图片(当这个图片在本次程序加载过),找到直接使用;

    从沙盒中找,找到直接使用,缓存到内存。

    从网络上获取,使用,缓存到内存,缓存到沙盒。

    55、@synthesize、@dynamic的理解

    @synthesize是系统自动生成getter和setter属性声明;@synthesize的意思是,除非开发人员已经做了,否则由编译器生成相应的代码,以满足属性声明;

    @dynamic是开发者自已提供相应的属性声明,@dynamic意思是由开发人员提供相应的代码:对于只读属性需要提供setter,对于读写属性需要提供 setter 和getter。查阅了一些资料确定@dynamic的意思是告诉编译器,属性的获取与赋值方法由用户自己实现, 不自动生成。

    56、UIViewController的完整生命周期

    -[ViewController initWithNibName:bundle:];

    -[ViewController init];

    -[ViewController loadView];

    -[ViewController viewDidLoad];

    -[ViewController viewWillDisappear:];

    -[ViewController viewWillAppear:];

    -[ViewController viewDidAppear:];

    -[ViewController viewDidDisappear:];

    57、#define定义的宏和const定义的常量有什么区别?

    #define定义宏的指令,程序在预处理阶段将用#define所定义的内容只是进行了替换。因此程序运行时,常量表中并没有用#define所定义的宏,系统并不为它分配内存,而且在编译时不会检查数据类型,出错的概率要大一些。

    const定义的常量,在程序运行时是存放在常量表中,系统会为它分配内存,而且在编译时会进行类型检查。

    #define定义表达式时要注意“边缘效应”,例如如下定义:

    #define N 2 + 3 // 我们预想的N值是5,我们这样使用N

    inta = N/ 2;  // 我们预想的a的值是2.5,可实际上a的值是3.5

    58、TCP和UDP的区别是什么

    TCP:面向连接、传输可靠(保证数据正确性,保证数据顺序传输)、用于传输大量数据(流模式)、速度慢,建立连接需要开销较多(时间,系统资源)。

    UDP:面向非连接、传输不可靠、用于传输少量数据(数据包模式)、速度快,传输的是报文。

    59、MD5和Base64的区别是什么,各自使用场景是什么?

    MD5:是一种不可逆的摘要算法,用于生成摘要,无法逆着破解得到原文。常用的是生成32位摘要,用于验证数据的有效性。比如,在网络请求接口中,通过将所有的参数生成摘要,客户端和服务端采用同样的规则生成摘要,这样可以防篡改。又如,下载文件时,通过生成文件的摘要,用于验证文件是否损坏。

    Base64:属于加密算法,是可逆的,经过encode后,可以decode得到原文。在开发中,有的公司上传图片采用的是将图片转换成base64字符串,再上传。在做加密相关的功能时,通常会将数据进行base64加密/解密。

    60、发送10个网络请求,然后再接收到所有回应之后执行后续操作,如何实现?

    从题目分析可知,10个请求要全部完成后,才执行某一功能。比如,下载10图片后合成一张大图,就需要异步全部下载完成后,才能合并成大图。

    做法:通过dispatch_group_t来实现,将每个请求放入到Group中,将合并成大图的操作放在dispatch_group_notify中实现。

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

    // 合并图片

    });

    61、对象添加到通知中心中,当通知中心发通知时,这个对象却已经被释放了,可能会出现什么问题?

    其实这种只是考查对通知的简单应用。通知是多对多的关系,主要使用场景是跨模块传值。当某对象加入到通知中心后,若在对象被销毁前不将该对象从通知中心中移除,当发送通知时,就会造成崩溃。这是很常见的。所以,在添加到通知中心后,一定要在释放前移除。

    62、这段代码有什么问题,如何修改

    for (int i = 0; i < someLargeNumber; i++) {NSString *string = @”Abc”;

    string = [string lowercaseString];

    string = [string stringByAppendingString:@"xyz"];NSLog(@“%@”, string);

    }

    会出现内存泄露

    修改之后:

    for(int i = 0; i<1000;i++){

    NSAutoreleasePool * pool1 = [[NSAutoreleasePool alloc] init];NSString *string = @"Abc";

    string = [string lowercaseString];

    string = [string stringByAppendingString:@"xyz"];NSLog(@"%@",string);

    //释放池

    [pool1 drain];}

    63、父类实现深拷贝时,子类如何实现深度拷贝。父类没有实现深拷贝时,子类如何实现深度拷贝。

    深拷贝同浅拷贝的区别:浅拷贝是指针拷贝,对一个对象进行浅拷贝,相当于对指向对象的指针进行复制,产生一个新的指向这个对象的指针,那么就是有两个指针指向同一个对象,这个对象销毁后两个指针都应该置空。深拷贝是对一个对象进行拷贝,相当于对对象进行复制,产生一个新的对象,那么就有两个指针分别指向两个对象。当一个对象改变或者被销毁后拷贝出来的新的对象不受影响。

    实现深拷贝需要实现NSCoying协议,实现- (id)copyWithZone:(NSZone *)zone 方法。当对一个property属性含有copy修饰符的时候,在进行赋值操作的时候实际上就是调用这个方法。

    父类实现深拷贝之后,子类只要重写copyWithZone方法,在方法内部调用父类的copyWithZone方法,之后实现自己的属性的处理

    父类没有实现深拷贝,子类除了需要对自己的属性进行处理,还要对父类的属性进行处理。

    64、将一个函数在主线程执行的4种方法

    GCD方法,通过向主线程队列发送一个block块,使block里的方法可以在主线程中执行。

    dispatch_async(dispatch_get_main_queue(), ^{

    //需要执行的方法

    });

    NSOperation 方法

    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];  //主队列

    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{

    //需要执行的方法

    }];

    [mainQueue addOperation:operation];

    NSThread 方法

    [self performSelector:@selector(method) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES modes:nil];

    [self performSelectorOnMainThread:@selector(method) withObject:nil waitUntilDone:YES];

    [[NSThread mainThread] performSelector:@selector(method) withObject:nil];

    RunLoop方法

    [[NSRunLoop mainRunLoop] performSelector:@selector(method) withObject:nil];

    65、id和NSObject*的区别

    id是一个 objc_object 结构体指针,定义是

    typedef struct objc_object *id

    id可以理解为指向对象的指针。所有oc的对象 id都可以指向,编译器不会做类型检查,id调用任何存在的方法都不会在编译阶段报错,当然如果这个id指向的对象没有这个方法,该崩溃还是会崩溃的。

    NSObject *指向的必须是NSObject的子类,调用的也只能是NSObjec里面的方法否则就要做强制类型转换。

    不是所有的OC对象都是NSObject的子类,还有一些继承自NSProxy。NSObject *可指向的类型是id的子集。

    66、static关键字的作用

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

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

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

    67、RunLoop是什么?

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

    最常见的需要开启Runloop的是在子线程里面调用计时器(NSTimer), 如果不开启runloop循环方法就不能正常执行.

    68、字符串替换方法

    [string stringByReplacingOccurrencesOfString:@"png" withString: @""]

    69、数组和指针的区别

    (1)数组可以申请在栈区和数据区;指针可以指向任意类型的内存块

    (2)sizeof作用于数组时,得到的是数组所占的内存大小;作用于指针时,得到的都是4个字节的大小

    (3)数组名表示数组首地址,值不可以改变,如不可以将++作用于数组名上;普通指针的值可以改变,如可将++作用于指针上

    (4)用字符串初始化字符数组是将字符串的内容拷贝到字符数组中;用字符串初始化字符指针是将字符串的首地址赋给指针,也就是指针指向了该数组

    70、在Obj-c中有没有私有方法?私有变量?一般采用什么方法实现?

    objective-c – 类里面的方法只有两种, 静态方法和实例方法. 这似乎就不是完整的面向对象了,按照OO的原则就是一个对象只暴露有用的东西. 如果没有了私有方法的话, 对于一些小范围的代码重用就不那么顺手了. 在类里面声名一个私有方法

    @interfaceController : NSObject { NSString *something; }

    +(void)thisIsAStaticMethod;

    -(void)thisIsAnInstanceMethod;

    @end

    @interfaceController (private)

    -(void)thisIsAPrivateMethod;

    @end

    @private可以用来修饰私有变量

    在Objective‐C中,所有实例变量默认都是私有的,所有实例方法默认都是公有的

    71、在终端环境下,分别说明4,2,1,0对应的权限是什么

    linux系统下文件的权限由三位二进制数表示,从左到右依次表示读、写、可执行。0表示不行,1表示行。4对应2进制的100,就是可读不可写不可执行。

    72、队列和栈有什么区别

    队列和栈是两种不同的数据容器。从”数据结构”的角度看,它们都是线性结构,即数据元素之间的关系相同。

    队列是一种先进先出的数据结构,它在两端进行操作,一端进行入队列操作,一端进行出列队操作。

    栈是一种先进后出的数据结构,它只能在栈顶进行操作,入栈和出栈都在栈顶操作。

    73、HTTP协议中,POST和GET的区别是什么?

    答案:1.GET 方法

    GET 方法提交数据不安全,数据置于请求行,客户端地址栏可见;

    GET 方法提交的数据大小有限

    GET 方法不可以设置书签

    2.POST 方法

    POST 方法提交数据安全,数据置于消息主体内,客户端不可见

    POST 方法提交的数据大小没有限制

    POST 方法可以设置书签

    74、链表逆序

    链表逆序就是把一个链表按照原来的链接顺序逆序实现(也就是将头变成尾,尾变成头)。

    编程思路:其实最关键的是先通过原来的链接顺序找到下个节点,然后再把前个节点反序。

    75、假定输入的字符串中只包含字母和* 号。编写函数fun,功能是,除了中间和尾部的*号外,

    将字符串中其他* 号全部删除。编写时,不用c的其他函数。

    例:*****A*BC*DEF*G****    结果为:A*BC*DEF*G****

    void fun (char *a){

    int j=0;

    char *p=a;

    while (*p==’*')p++;

    while (*p){

    a[j++]=*p;

    p++;

    }

    a[j]=0;

    }

    76、objective-c 中的数字对象都有哪些,简述它们与基本数据类型的区别是什么?

    在OC 中NSNumber是数字对象,可以进行拆装箱操作!

    // 将int 转为NSNumber

    NSNumber *num = [NSNumber numberWithInt:123];

    // 得到一个int

    inttestNum = [numintValue];

    77、do-while 与 while-do 的区别?

    do-while 先执行循环体,然后判断条件,如果条件判断为ture ,则继续执行循环体,如果判断为false,则不执行循环体

    while-do 是先判断条件是否正确,若正确则执行循环体,若不正确则不执行循环体。

    所以do-while 至少循环一次,而while-do有可能一次也不循环。

    78、类变量的@protected ,@private,@public,@package,声明各有什么含义?

    @private:作用范围只能在自身类

    @protected:作用范围在自身类和继承自己的子类  (默认)

    @public:作用范围最大,可以在任何地方被访问。

    @package:这个类型最常用于框架类的实例变量,同一包内能用,跨包就不能访问

    79、谈谈你对多线程开发的理解?ios中有几种实现多线程的方法?

    好处:

    1.使用线程可以把占据时间长的程序中的任务放到后台去处理

    2.用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度

    3.程序的运行速度可能加快

    4·在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。

    缺点:

    1.如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换。

    2.更多的线程需要更多的内存空间。

    3.线程的中止需要考虑其对程序运行的影响。

    4.通常块模型数据是在多个线程间共享的,需要防止线程死锁情况的发生。

    实现多线程的方法:NSObject类方法、NSThread、NSOperation、GCD

    80、假设有一个字符串aabcad,请写一段程序,去掉字符串中不相邻的重复字符串,即上述字符串处理之后的输出结果为:aabcd

    1、NSMutableString * str = [[NSMutableString alloc]initWithFormat;@“aabcad”];

    for (int i = 0 ,i < str.length - 1 ;i++){

        unsigned char a = [str characterAtIndex:i];

        for (int j = i + 1 ,j < str.length ,j++){

            unsigned char b = [str characterAtIndex:j];

            if (a == b ){

                if (j == i + 1){

                }else{

                    [str deleteCharactersInRange:NSMakeRange(j, 1)];

                }

            }

        }

    }

    NSLog(@“%@”,str);

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

    alloc申请内存时调用

    loadView加载视图时调用

    ViewDidLoad视图已经加载后调用

    ViewWillAppear视图将要出现时调用

    ViewDidUnload视图已经加载但没有加载出来调用

    dealloc销毁该视图时调用

    init视图初始化时调用

    82、应用如何找到最合适的控件来处理事件?

    1.首先判断主窗口(keyWindow)自己是否能接受触摸事件

    2.判断触摸点是否在自己身上

    3.子控件数组中从后往前遍历子控件,重复前面的两个步骤(所谓从后往前遍历子控件,就是首先查找子控件数组中最后一个元素,然后执行1、2步骤)

    4.view,比如叫做fitView,那么会把这个事件交给这个fitView,再遍历这个fitView的子控件,直至没有更合适的view为止。

    5.如果没有符合条件的子控件,那么就认为自己最合适处理这个事件,也就是自己是最合适的view

    83、事件的传递和响应的区别:

    事件的传递是从上到下(父控件到子控件),事件的响应是从下到上(顺着响应者链条向上传递:子控件到父控件。

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

    UIView和CALayer都是源自NSObject。

    1、UIView的继承结构为:UIResponder:NSObject。可以看出UIView的直接父类为UIResponder类。

    可见UIResponder是用来响应事件的,也就是说UIView可以响应用户事件。

    2.CALayer的继承结构:NSObject

    直接从NSObject继承,因为缺少了UIResponder类,所以CALayer不能响应任何的用户事件。

    它们分别所属的框架

    1.UIView是在/System/Library/Frameworks/UIKit.framework中定义的。

    我们都知道UIKit主要是用来构建用户界面并且是可以响应事件的。

    2.CALayer是在/System/Library/Frameworks/QuartzCore.framework定义的。而且CALayer作为一个低级的可以承载绘制内容的底层对象出现在该框架中。

    综上来看UIView与CALayer的最大区别在于UIView可以响应用户事件,而CALayer不可以。UIView侧重于对显示内容的管理,CALayer侧重于对内容的绘制。

    由此可见UIView确实是CALayer 的高级封装。

    UIView和CALayer相互依赖,UIView依赖于CALayer提供的内容,CALayer依赖UIView提供的容器来显示绘制的内容。但归根到底CALayer是这一切的基础,如果没有CALayer,UIView自身也不会存在,UIView是一个特殊的CALayer的实现,添加了响应事件的能力。

    UIwindow是UIView的子类,UIWindow的主要作用:一是提供一个区域来显示UIView,二是将事件(event)的分发给UIView,一个应用基本上只有一个UIWindow.

    iOS程序启动完毕后,创建的第一个视图控件就是UIWindow,接着创建控制器的view,最后将控制器的view添加到UIWindow上,于是控制器的view就显示在屏幕上了。一个iOS程序之所以能显示到屏幕上,完全是因为它有UIWindow。即没有UIWindow,就看不见任何UI界面。

    85、简述声明一个属性时,使用copy与strong的区别

    1) 当原字符串是NSString时,字符串是不可变的,不管是Strong还是Copy属性的对象,都是指向原对象,Copy操作只是做了次浅拷贝。

    2) 当原字符串是NSMutableString时,Strong属性只是增加了原字符串的引用计数,而Copy属性则是对原字符串做了次深拷贝,产生一个新的对象,且Copy属性对象指向这个新的对象,且这个Copy属性对象的类型始终是NSString,而不是NSMutableString,因此其是不可变的。

    3) 这里还有一个性能问题,即在原字符串是NSMutableString,Strong是单纯的增加对象的引用计数,而Copy操作是执行了一次深拷贝,所以性能上会有所差异(虽然不大)。如果原字符串是NSString时,则没有这个问题。

    所以,在声明NSString属性时,到底是选择strong还是copy,可以根据实际情况来定。不过,一般我们将对象声明为NSString时,都不希望它改变,所以大多数情况下,我们建议用copy,以免因可变字符串的修改导致的一些非预期问题。

    86、objc中的类方法和实例方法有什么区别

    实例方法:实例出的对象所用的方法,原理是向某个对象发送一条消息,如果对象中有相应的消息就会做出回应,OC用的就是这种消息模式.

    类方法:也叫静态方法,不需要申明一个对象,可以直接调用,一般是有返回值的,其主要作用就是返回一个对应的实例(如数组,字符串或者就是本类的一个对象等),

    类方法

    类方法属于类对象

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

    类方法中的self是类对象

    类方法可以调用其他类方法

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

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

    实例方法

    实例方法是属于实例对象的

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

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

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

    实例方法中直接调用实例方法

    实例方法中也可以调用类方法(通过类名)

    87、如果gcd创建3个任务,如何取消最后一个

    GCD原生并不支持取消操作。

    dispatch_suspend函数也只能暂停开启新的未执行的block,已经处于执行中的block是无法暂停的。

    但是,通过参考NSOperation的cancel机制,你只要加一个外边变量,用于标记block是否需要取消。然后block中通过及时的检测这个外部变量的状态,当发现需要取消时,停止block中的后续操作,释放资源。就能达到及时取消block的目的。这里有个例子:

    https://github.com/Tinghui/HUIGCDDispatchAsync

    88、请简述AFNetWorking的实现原理

    NSURLConnection + NSOperation

    NSURLConnection 是 Foundation URL 加载系统的基石。一个 NSURLConnection 异步地加载一个NSURLRequest 对象,调用 delegate 的 NSURLResponse / NSHTTPURLResponse 方法,其 NSData 被发送到服务器或从服务器读取;delegate 还可用来处理 NSURLAuthenticationChallenge、重定向响应、或是决定 NSCachedURLResponse 如何存储在共享的 NSURLCache 上。

    NSOperation 是抽象类,模拟单个计算单元,有状态、优先级、依赖等功能,可以取消。

    AFNetworking 的第一个重大突破就是将两者结合。AFURLConnectionOperation 作为 NSOperation 的子类,遵循 NSURLConnectionDelegate 的方法,可以从头到尾监视请求的状态,并储存请求、响应、响应数据等中间状态。

    89、下面的代码输出什么?

    @implementation Son : Father

    - (id)init

    {

    self = [super init];

    if (self) {

    NSLog(@"%@", NSStringFromClass([self class]));

    NSLog(@"%@", NSStringFromClass([super class]));

    }

    return self;

    }

    @end

    2016-05-13 17:52:51.694 Test[12816:262134] Son

    2016-05-13 17:52:51.696 Test[12816:262134] Son

    原因:调用父类初始化的时候,是不看指针看对象的,因此谁调用super谁就是super的拥有者

    90、SDWebImage的原理

    1.入口

    setImageWithURL:placeholderImage:options: 会先把placeholderImage展示,然后 SDWebImageManager根据URL开始处理图片.

    2.进入

    SDWebImageManager-downloadWithURL:delegate:options:userInfo:,交给SDImageCache从缓存查找图片是否已经下载 :

    通过: queryDiskCacheForKey:delegate:userInfo:.

    3.先从内存图片缓存查找是否有图片,如果内存中已经有图片缓存SDWebImageDelegate回调imageCache:didFindImage:ForKey:userInfo: 到SDWebImageManager

    4.SDWebImageManagerDelegate回调

    webImageManager:didFinishWithImage:

    到UIImageView+WebCache等前端展示图片

    5.如果内存缓存中没有,生成NSInvocationOperation添加到队列开始从硬盘查找图片是否已经缓存。

    6.根据 URLKey 在硬盘缓存目录下尝试读取图片文件.这一步是在 NSOperation 进行的操作,所以回主线程进行结果回调.

    7.如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中,如果内存空间过小会先清空内存缓存.

    SDImageCacheDelegate回调 imageCache:didFindImage:forKey:userInfo: 进而回调展示图片.

    8.如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调imageCache:didNotFindImageForKey:userInfo:。

    9.共享或重新生成一个下载器 SDWebImageDownloder开始下载图片

    10.图片下载由 NSURLConnection来做,实现相关的Delegate来判断图片下载中,下载完成和下载失败.

    11.connection:didReceiveData:中利用 ImageIO 做了按图片下载进度加载效果。

    12.connectionDidFinishLoading: 数据下载完成后交给 SDWebImageDecoder 做图片解码处理。

    13.图片解码处理在一个 NSOperationQueue 完成,不会拖慢主线程 UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。

    14.在主线程notifyDelegateOnMainThreadWithInfo:宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo:回调给 SDWebImageDownloader。

    15.imageDownloader:didFinishWithImage: 回调给 SDWebImageManager 告知图片下载完成。

    16.通知所有的 downloadDelegates 下载完成,回调给需要的地方展示图片。

    17.将图片保存到 SDImageCache 中,内存缓存和硬盘缓存同时保存。写文件到硬盘也在以单独 NSInvocationOperation 完成,避免拖慢主线程。

    18.SDImageCache 在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片。

    19.SDWI 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache,方便使用。

    20.SDWebImagePrefetcher 可以预先下载图片,方便后续使用

    91、BAD_ACCESS在什么情况下出现?如何调试BAD_ACCESS错误?

    什么是 EXC_BAD_ACCESS?

    不管什么时候当你遇到EXC_BAD_ACCESS这个错误,那就意味着你向一个已经释放的对象发送消息。访问了野指针,比如对一个已经释放的对象执行了release、访问已经释放对象的成员变量或者发消息。

    EXC_BAD_ACCESS的本质

    技术层面的解释有些复杂。在C和Objective-C中,你一直在处理指针。指针无非是存储另一个变量的内存地址的变量。当您向一个对象发送消息时,指向该对象的指针将会被引用。这意味着,你获取了指针所指的内存地址,并访问该存储区域的值。

    当该存储器区域不再映射到您的应用时,或者换句话说,该内存区域在你认为使用的时候却没有使用,该内存区域是无法访问的。 这时内核会抛出一个异常( EXC ),表明你的应用程序不能访问该存储器区域(BAD ACCESS) 。

    总之,当你碰到EXC_BAD_ACCESS ,这意味着你试图发送消息到的内存块,但内存块无法执行该消息。但是,在某些情况下, EXC_BAD_ACCESS是由被损坏的指针引起的。每当你的应用程序尝试引用损坏的指针,一个异常就会被内核抛出。

    调试EXC_BAD_ACCESS

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

    2.通过 Zombie

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

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

    92、找错误(1)

    char * GetMemory(void){

    char p[] = "Hello World";

    return p;

    }

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

    @autoreleasepool {

    char * str = NULL;

    str = GetMemory();

    printf("%s\n",str);

    }

    return 0;

    }

    错误为:

    char p[] = "Hello World";

    return p;//不能返回栈地址,因为栈空间自动释放,应该返回栈空间的数据

    正确应该为:

    char *p = "Hello World";

    93、找错误(2)

    void GetMemory(char **p, int num){

    *p = (char *)malloc(num);

    }

    void Test(void){

    char * str = NULL;

    GetMemory(&str, 100);

    strcpy(str, "Hello");

    printf(str);

    }

    // 堆空间需要手动释放,申请了堆内存时也要判断是否申请成功

    char * str = NULL;

    GetMemory(&str, 100);

    if(str) {

    strcpy(str, "Hello");

    printf("%s",str);

    free(str);

    str = NULL;

    }

    94、写出下列两个属性的Setter方法

    @property (nonatomic, retain) NSString * name;

    @property (nonatomic, copy) NSString * name;

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

    if( _name != name) {

     [_name release];

    _name = [name retain];

    }

    }

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

    _name = [name copy];

    }

    retain修饰的属性setter方法的实现步骤:

    1:判断新值与旧值是否相等,如果不等执行以下操作

    2:将旧值执行一次release操作(旧值release)

    3:再将新值执行一次retain操作再赋给旧值(新值retain再赋值)

    copy修饰的属性:如果要保证返回的是一个不可变的版本就要将新值执行一次copy操作

    95、类别和继承什么区别

    1.类别是对方法的扩展,不能添加成员变量。继承可以在原来父类的成员变量的基础上,添加新的成员变量

    2.类别只能添加新的方法,不能修改和删除原来的方法。继承可以增加、修改和删除方法。

    3.类别不提倡对原有的方法进行重载。继承可以通过使用super对原来方法进行重载。

    4.类别可以被继承,如果一个父类中定义了类别,那么其子类中也会继承此类别。

    96、线程与进程的区别和联系

    进程: 进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时,它才能成为一个活动的实体,我们称其为进程。进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体。它不只是程序的代码,还包括当前的活动,通过程序计数器的值和处理寄存器的内容来表示。

    线程: 通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度。

    线程与进程的区别:

    a.地址空间和其它资源:进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。

    b.通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。

    c.调度和切换:线程上下文切换比进程上下文切换要快得多。

    d.在多线程OS中,进程不是一个可执行的实体

    97、数组去重有哪些方式

    1)、利用NSDictionary的AllKeys(AllValues)方法

    NSMutableDictionary *dic = [[NSMutableDictionary alloc]initWithCapacity:0];

    for(NSString *str in dataArray){

    [dic setValue:str forKey:str];

    }

    2)、利用NSSet的AllObjects方法

    NSSet *set = [NSSet setWithArray:dataArray];

    NSLog(@"%@",[set allObjects]);

    3)、利用数组的containsObject来去除

    NSMutableArray *listAry = [[NSMutableArray alloc]init];

    for (NSString *str in dataArray) {

    if (![listAry containsObject:str]) {

    [listAry addObject:str];

    }

    }

    98、同时实现一个property 属性的setter 和 getter的方法,调用_ivar会报错

    在.h或者.m文件中用@property声明一个属性时。如果同时重写getter和setter方法,会报“该变量没有定义的错误”。

    解决办法:.m文件中需要加上:

    @synthesize propertyName = _propertyName;

    原因:因为@property默认给该属性生成getter和setter方法,当getter和setter方法同时被重写时,则系统就不会自动生成getter和setter方法了,也不会自动帮你生成_num变量,所以不会识别。

    小知识:声明的property如果没有特意指定synthesize的话,那么Objective-C就会自动的给你声明一个_开头的实例变量。

    _xxx访问的是xxx的地址。self.xxx访问的是xxx的getter。这两者并不是完全等价的,self.xxx是用objc_msgSend发消息,_xxx或者self->xxx则是直接访问内存地址,一般建议在init里面用_xxx,其他地方用self.xxx.

    99、@property (copy) NSMutableArray *array;这个写法会有什么问题?

    即使在MRC时代,我们通常会写成retain,在ARC时代,我们会写成strong,所以copy会略显奇怪

    尝试分析一下,copy为内容拷贝,和源对象变成2个拥有相同内容的不同指针,互不影响.

    并且因为调用copy方法,所以该array属性在RunTime的时候,实际上为NSArray,即无法调用add等方法,但是编译期调用是合法的,所以可能会造成crash.

    100、@property的本质是什么?ivar、getter、setter是如何生成并添加到这个类的

    @property = ivar + getter + setter

    “属性”(@property)有两大概念:实例变量(ivar)、存取方法(access method = getter + setter)。

    “属性” (property)作为 Objective-C 的一项特性,主要的作用就在于封装对象中的数据。 objective-c 对象通常会把其所需要的数据保存为各种实例变量。实例变量一般通过“存取方法”(access method)来访问。

    ivar、 getter、setter是如何生成并添加到这个类的?

    自动合成(autosynthesis)

    完成属性定以后,编译器会自动编写访问这些属性所需的方法,此过程叫做“自动合成”(autosynthesis)。需要强调的是,这一过程是在编译期间执行的。除了生成方法代码getter、setter之外,编译器还要自动向类中添加适当的类型的实例变量,并且属性名前面添加下划线。

    相关文章

      网友评论

      本文标题:iOS开发笔试-100道

      本文链接:https://www.haomeiwen.com/subject/vwnsqxtx.html