iOS面试杂记

1. 函数局部变量的return

R:一般的来说,函数是可以返回局部变量的。 局部变量的作用域只在函数内部,在函数返回后,局部变量的内存已经释放了。因此,如果函数返回的是局部变量的值,不涉及地址,程序不会出错。但是如果返回的是局部变量的地址(指针)的话,程序运行后会出错。因为函数只是把指针复制后返回了,但是指针指向的内容已经被释放了,这样指针指向的内容就是不可预料的内容,调用就会出错。准确的来说,函数不能通过返回指向栈内存的指针(注意这里指的是栈,返回指向堆内存的指针是可以的)。不管是返回指针还是返回值,return将return之后的值存到eax寄存器中,回到父函数再将返回的值赋给变量。

参考:C语言中函数的思考:返回局部变量

2. NSString地址
@“hello”和[NSString stringWithFormat:@"hello"]有何区别?

NSString *A = @"hello";
NSString *B = @"hello";
NSString *C = [NSString stringWithFormat:@"hello"];
NSString *D = [NSString stringWithFormat:@"hello"];

@“hello”位于常量池,可重复使用,其中A和B指向的都是同一份内存地址。
而[NSString stringWithFormat:@"hello"]是运行时创建出来的,保存在运行时内存(即堆内存),其中C和D指向的内存地址不同,与A、B也不相同。
2. 手写单例

单例用途:限制创建,提供全局调用,节约资源和提高性能
static的作用:防止外部访问
@synchronized的作用:为了防止多线程同时访问对象,造成多次分配内存空间,所以要加上线程锁。单线程操作可以不用。

系统单例有: UIApplication、NSNotificationCenter、NSFileManager、NSUserDefaults、NSURLCache 、NSHTTPCookieStorage等
参考: iOS Principle:Singleton

#import "SingleInstance.h"

@implementation SingleInstance
// 类方法命名规范 share类名|default类名|类名
+(id)sharedInstance {
    // 创建静态对象 防止外部访问
    static SingleInstance *instance;
    //    @synchronized (self) {
    //        // 为了防止多线程同时访问对象,造成多次分配内存空间,所以要加上线程锁。 
    //       //  相比于GCD,@synchronized比较耗性能
    //        if (instance == nil) {
    //            instance = [[super allocWithZone:NULL] init];
    //        }
    //        return _instance;
    //    }
    // 也可以使用一次性代码
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // super 避免循环调用
        instance = [[super allocWithZone:NULL] init];
    });
    return instance;
}
// 为了严谨,也要重写allocWithZone 和 copyWithZone
+(id)allocWithZone:(struct _NSZone *)zone {
    // 最好用self 用SingleInstance他的子类调用时会出现错误
    return [self sharedInstance];
}

-(id)copyWithZone:(nullable NSZone *)zone {
    return self;
}

@end
延伸问题:单例模式如何销毁?

有时候会有一种场景,需要销毁单例进行重建。
但多数需要销毁的单例实际上可能不适用于单例这种模式,可能需要重新考虑架构设计问题。

// SingleInstance *instance;  dispatch_once_t onceToken; 声明为全局静态变量
// 了解到 dispatch_once_t参数的初始值就是0l,只需要重置dispatch_once_t参数及实例参数即可
+ (void)tearDown{
  instance=nil;
  onceToken=0l;
}
3. 代码执行顺序
// 对象 A
- (void)_aLog {    
    NSLog(@"1");
   // 发送通知
    [[NSNotificationCenter defaultCenter] postNotificationName:@"NSLog(2)" object:nil userInfo:nil];

    NSLog(@"3");
}

// 观察者对象B 接收通知去执行selector(log)
-(void)_bLog {
    NSLog(@"2");
}

执行对象A的_aLog方法,打印结果: 1 2 3

通知的响应回调是同步执行的。
NSNotification接受线程是基于发送消息的线程的,即发送消息和接收消息 是在同一个线程
参考:iOS多线程中使用NSNotification

// 对象 A
- (void)_aLog {    
    NSLog(@"1");
   // 执行performSelector相关方法
    [self performSelector:@selector(log2) withObject:nil afterDelay:0];
    [self performSelector:@selector(log3) withObject:nil];
    NSLog(@"4");
}
-(void)log2 {
    NSLog(@"2");
}
-(void)log3 {
    NSLog(@"3");
}
执行对象A的_aLog方法,打印结果: 1 3 4 2

[self performSelector:@selector(test) withObject:nil afterDelay:0] 是NSRunLoop.h的方法,是单线程的也就是说只有当前调用此方法的函数执行完毕后,selector方法才会被调用。
[self performSelector:@selector(log3) withObject:nil];是NSObject.h文件下的方法,会走正常调用顺序。

- (void)_aLog {
     dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
     dispatch_async(queue, ^{
        NSLog(@"1");
        [self performSelector:@selector(log2) withObject:nil afterDelay:0.0];
        NSLog(@"3");
    });
}
-(void)log2 {
    NSLog(@"2");
}
执行对象A的_aLog方法,打印结果: 1 3 

因为[self performSelector:@selector(test) withObject:nil afterDelay:.0]实际在runloop里面是一个定时器,但是因为在子线程,runloop是默认没有开启的。所以该方法不会被调用。
参考:关于performSelector:afterDelay:的一个坑及思考

4. CoreFundation 和 Fundation

Fundation 是 CoreFundation 一种包装,在MRC底层数据结构是一样的.
在MRC情况下 CF对象和NS对象是一个东西.

当在ARC情况下需要用到CF对象时可以利用__bridge来转换对象:

__bridge ->内存管理者不切换 ,即 用CF类构造函数创造的对象仍然需要手动release , OC类构造函数创建的对象可以自动释放.下面同理.

__bridge_transfer/CFBridgingRelease ->把CF对象转换成NS对象,并且内存管理者切换 , 即 CF对象转换为OC对象后,可以被自动释放.

__bridge_retained/CFBridgingRetain -> 把NS对象转换成CF对象,并且内存管理者切换.

5. 关于alloc 、init和new

alloc: 为对象的所有成员变量分配内存空间,并且为各个成员变量重置为默认值,如int型为0,BOOL型为NO,指针型变量为nil。使对象不被释放,将地址返回给指针。
init: 为分配好的内存进行初始化。好比拿到地皮要规划盖楼
new:等同[alloc]init,实际上[alloc]init在alloc的时候调用allocWithZone分配了内存,然后调用init初始化。而new在分配内存后直接调用init进行初始化。
new不常使用因为它只能init,而alloc init却有更多实用和可定制的初始化方法。
alloc ,init分配的内存会和相关联的对象在内存地址中相靠近,利于内存读取,调用时消耗很少的内存,提升了程序处理速度
alloc & allocwithZone :alloc会默认调用allocWithZone方法

6. 沙盒

每个iOS程序都有一个独立的文件系统(存储空间),而且只能在对应的文件系统中进行操作,此区域被称为沙盒。应用必须待在自己的沙盒里,其他应用不能访问该沙盒。所有的非代码文件都要保存在此,例如属性文件plist、文本文件、图像、图标、媒体资源等。

Documents/ 保存应用程序的重要数据文件和用户数据文件等。用户数据基本上都放在这个位置(例如从网上下载的图片或音乐文件),该文件夹在应用程序更新时会自动备份,在连接iTunes时也可以自动同步备份其中的数据。

Library:这个目录下有两个子目录,可创建子文件夹。可以用来放置您希望被备份但不希望被用户看到的数据。该路径下的文件夹,除Caches以外,都会被iTunes备份.

Library/Caches: 保存应用程序使用时产生的支持文件和缓存文件(保存应用程序再次启动过程中需要的信息),还有日志文件最好也放在这个目录。iTunes 同步时不会备份该目录并且可能被其他工具清理掉其中的数据。
Library/Preferences: 保存应用程序的偏好设置文件。NSUserDefaults类创建的数据和plist文件都放在这里。会被iTunes备份。

tmp/: 保存应用运行时所需要的临时数据。不会被iTunes备份。iPhone重启时,会被清空。

7.关于一个int *p=(int *)(&a + 1);
#include <stdio.h>
 int main ()
{
    int a[5] = {1,2,3,4,5};
    int *p = (int*)(&a + 1);//&a表示整个数组的地址
    printf("%d %d" , *(a + 1), *(p - 1));
}
//输出结果为:2,5
*(a+1)其实很简单就是指a[1],输出为2.
问题关键就在于第二个点,*(p-1)输出为多少?

解释如下
&a+1不是首地址+1,系统会认为加了一个整个a数组,偏移了整个数组a的大小(也就是5个int的大小)。所以int*ptr=(int*)(&a+1);其实p实际是&(a[5]),也就是a+5.

原因为何呢?
&a是数组指针,其类型为int(*)[5];而指针+1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同,a是长度为5的int数组指针,所以要加5*sizeof(int),所以p实际是a[5],但是p与(&a + 1)类型是不一样的,这点非常重要,所以p - 1只会减去sizeof(int*),a,&a的地址是一样的,但意思就不一样了,a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址,a+1是数组下一元素的地址,即a[1],&a + 1是下一个对象的地址,即a[5]。

8. 关闭控件响应事件的方法
方法1. 属性userInteractionEnabled设成No
方法2. hidden属性设成yes
方法3. alpha在0~0.01之间
方法4. 重写控件将hitTest:withEvent:方法,将返回置为空(因为响应者链条是这个方法传递的)。
9. 防止UIButton重复点击的方法
方法1. 使用UIButton的enabled或userInteractionEnabled
比如:使用UIButton的enabled属性, 在点击后, 禁止UIButton的交互, 直到完成指定任务之后再将其enable即可

方法2. 使用performSelector:withObject:afterDelay:和cancelPreviousPerformRequestsWithTarget
使用这种方式, 会在连续重复点击UIButton的时候, 自动取消掉之前的操作, 延时1s后执行实际的操作.

方法3. 使用runtime来对sendAction:to:forEvent:方法进行hook
UIControl的sendAction:to:forEvent:方法用于处理事件响应.
如果我们在该方法的实现中, 添加针对点击事件的时间间隔相关的处理代码, 则能够做到在指定时间间隔中防止重复点击.

参考:防止UIButton重复点击的三种实现方式

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,084评论 1 32
  • 1.设计模式是什么? 你知道哪些设计模式,并简要叙述?设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型...
    龍飝阅读 2,133评论 0 12
  • 1,NSObject中description属性的意义,它可以重写吗?答案:每当 NSLog(@"")函数中出现 ...
    eightzg阅读 4,131评论 2 19
  • 前言:面试笔试都是必考语法知识点。请认真复习和深入研究OC。 目录:iOS-面试题-OC基础篇 (1) - (84...
    麦穗0615阅读 4,245评论 0 33
  • 昨天特别开心!因为昨天我看到同学们在教室里一个个演童话故事。 第一个童话故事是《三只小猪》盛歆焯演猪老大,蔡睿杰演...
    浩_7bb5阅读 468评论 0 4