面试题小集

1、字符串反转

void char_reverse (char *cha){
    char *begin = cha;
    char *end = cha + strlen(cha) - 1;
    while (begin < end){
    char temp = *begin;
    *(begin++) = *end;
    *(end --) = temp;
    }
}

char ch[] = "hello Word";
char_reverse (ch);
print("%s",ch);

2、链表反转(头差法)

#import <Foundation/Foundation.h>

// 定义一个链表
struct Node {
    int data;
    struct Node *next;
};

@interface ReverseList : NSObject

// 链表反转
struct Node* reverseList(struct Node *head);

// 构造一个链表
struct Node* constructList(void);

// 打印链表中的数据
void printList(struct Node *head);

@end

#import "ReverseList.h"

@implementation ReverseList

struct Node* reverseList(struct Node *head)
{
    // 定义遍历指针,初始化为头结点
    struct Node *p = head;
    
    // 反转后的链表头部
    struct Node *newH = NULL;
    
    // 遍历链表
    while (p != NULL) {
        
        // 记录下一个结点
        struct Node *temp = p->next;
        // 当前结点的next指向新链表头部
        p->next = newH;
        // 更改新链表头部为当前结点
        newH = p;
        // 移动p指针
        p = temp;
    }
    
    // 返回反转后的链表头结点
    return newH;
}

struct Node* constructList(void)
{
    // 头结点定义
    struct Node *head = NULL;
    // 记录当前尾结点
    struct Node *cur = NULL;
    
    for (int i = 1; i < 5; i++) {
        struct Node *node = malloc(sizeof(struct Node));
        node->data = i;
        
        // 头结点为空,新结点即为头结点
        if (head == NULL) {
            head = node;
        }
        // 当前结点的next为新结点
        else{
            cur->next = node;
        }
        
        // 设置当前结点为新结点
        cur = node;
    }
    
    return head;
}

void printList(struct Node *head)
{
    struct Node* temp = head;
    while (temp != NULL) {
        printf("node is %d \n", temp->data);
        temp = temp->next;
    }
}

@end

3、iOS内存管理的理解
1、TaggedPointer (针对类似于NSNumber的小对象类型)
2、NONPOINTER_ISA(64位系统下)
第一位的0或1代表的是纯地址型isa指针,还是NONPOINTER_ISA指针
第二位,代表是否有关联对象
第三位代表是否有C++代码
接下来33位代码指向的内存地址
接下来有弱引用的标记
接下来有是否delloc的标记等等
3.散列表(引用计数表、weak表)
sideTables表在非嵌入式等64位系统中,有64张sideTable表
每一张sideTable主要由三部分组成。自旋锁、引用计数表、弱引用表
全局的引用计数之所以不存在同一张表中,是为了避免资源竞争,解决效率的问题
引用计数表中引入了分离锁的概念,将一张表分拆成多个部分,对他们分别加锁,可以实现并发操作,提升执行效率。

4、使用自动引用计数遵循原则
不能使用retaim 、release 、retainCount、autorelease
不可以使用NSAllocateObject、NSDeallocateObject
必须遵守内存管理方法的命名规则
不需要显示的调用dealloc
使用@autoreleasePool代替NSAutoreleasePool
不可以使用区域NSZone
对象性变量不可以作为C语言的结构体成员
显示转换id和void*

5、ARC自动内存管理原则
自己生成自己持有,自己持有对象不需要时,需要对其进行释放
非自己生成的对象,自己可以持有,但是不能释放

6、访问__weak 修饰的变量,是否已经被注册在了@autoreleasePool中?
肯定注册了,__weak修饰的变量属于弱引用,如果没有被注册到@autoreleasePool中,创建后也就会随之被销毁,为了延长它的生命周期,必须注册到@autoreleasePool中,以延缓释放

7、ARC的retainCount怎么存储的
存在64张哈希表中,根据哈希算法去查找所在的位置,无需遍历,十分快捷
散列表(引用计数表、weak表)
通过指针的地址,查找到引用计数的地址,大大提升查找效率
通过 DisguisedPtr(objc_object) 函数存储,同时也通过这个函数查找,这样就避免了循环遍历。

8、如果以通用的方法找到当前显示的ViewController?

+(UIViewController *)getCurrentVC {
    
    UIViewController *topRootViewController = [[UIApplication  sharedApplication] keyWindow].rootViewController;
    
    // 在这里加一个这个样式的循环
    while (topRootViewController.presentedViewController)
    {
        // 这里固定写法
        topRootViewController = topRootViewController.presentedViewController;
    }
    
    /*
     *  在此判断返回的视图是不是你的根视图--我的根视图是tabbar
     */
    if ([topRootViewController isKindOfClass:[UITabBarController class]]) {
        UITabBarController *mainTabBarVC = (UITabBarController *)topRootViewController;
        topRootViewController = [mainTabBarVC selectedViewController];
        topRootViewController = [topRootViewController.childViewControllers lastObject];
    }else{
        //导航堆栈
        topRootViewController =  topRootViewController.childViewControllers.lastObject;
    }
    
    return topRootViewController;
    
}

9、信号量
dispatch_semaphore_create: 创建一个信号量,具有整形的数值,即为信号的总量

dispatch_semaphore_signal :
返回值为long类型,当返回值为0时,表示当前并没有线程等待其处理的信号量,其处理的信号总量增加1。
当返回值不为0时,表示其当前有一个或者多个线程等待其处理的信号量,并且该函数唤醒了一个等待的线程(当线程有优先级的时候,唤醒优先级最高的线程,否则随机唤醒)。

dispatch_semaphore_wait:
等待信号,具体操作是首先判断信号量desema是否大于0,如果大于0就减掉1个信号,往下执行;
如果等于0函数就阻塞该线程等待timeout(注意timeout类型为dispatch_time_t)时,其所处线程自动执行其后的语句。

for (int i=0; i<10; i++)
    {
        dispatch_semaphore_t single = dispatch_semaphore_create(0);
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSLog(@"state%d",i);
            dispatch_semaphore_signal(single);
            NSLog(@"after%d",i);
        });
        dispatch_semaphore_wait(single, DISPATCH_TIME_FOREVER);
        NSLog(@"end%d",i);
    }
    NSLog(@"end");

理解:当信号量设置为0 的时候,即该线程没有并发,只能一个一个走完,当信号量为1的时候,即该线程仅允许一个并发,信号量的个数,控制着该线程的并发量,如果是期望该线程线性执行,则设置信号量为0

10、条件锁-NSContionLock

@interface NSConditionLock : NSObject <NSLocking> {
@private
    void *_priv;
}

- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;

@property (readonly) NSInteger condition;
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;

@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);

@end

NSConditionLock 和 NSLock类似,都遵循NSLocking协议,方法都类似,只是多了一个condition属性,以及每个操作都多了一个关于condition属性的方法,例如tryLock,tryLockWhenCondition NSConditionLock可以称为条件锁,只有condition参数与初始化时候多condition相等,lock才能正确进行加锁的操作。而unlockWithCondition并不是condition符合条件时才解锁,而是解锁之后,修改condition的值

11、递归锁 - NSRecursiveLock

@interface NSRecursiveLock : NSObject <NSLocking> {
@private
    void *_priv;
}

- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;

@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);

@end

NSRecursiveLock 是递归锁,他和 NSLock 的区别在于,NSRecursiveLock 可以在一个线程中重复加锁(反正单线程内任务是按顺序执行的,不会出现资源竞争问题),NSRecursiveLock 会记录上锁和解锁的次数,当二者平衡的时候,才会释放锁,其它线程才可以上锁成功。

12、同步锁 -Synchronized(self){}
@synchronized(object) 指令使用的 object 为该锁的唯一标识,只有当标识相同时,才满足互斥。
@synchronized(object) 简化了加锁的行为,我们不在需要显示的加锁。

13、自旋锁 - OSSpinLock
自旋锁是一种忙等的锁,用于轻量访问,比如在引用计数表 和 原子性 atomic
如果当前线程的锁被其他线程获取,当前线程会不断探索锁是否被释放,如果检测出释放,会第一时间获取这个锁

14、互斥锁 -pthread_mutex

// 初始化方法()
int pthread_mutex_init(pthread_mutex_t * __restrict, const pthread_mutexattr_t * __restrict);
// pthread_mutex_t * __restrict 代表互斥锁的类型,有以下四种
1.PTHREAD_MUTEX_NORMAL 缺省类型,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后先进先出原则获得锁。
2.PTHREAD_MUTEX_ERRORCHECK 检错锁,如果同一个线程请求同一个锁,则返回 EDEADLK,否则与普通锁类型动作相同。这样就保证当不允许多次加锁时不会出现嵌套情况下的死锁。
3.PTHREAD_MUTEX_RECURSIVE 递归锁,允许同一个线程对同一个锁成功获得多次,并通过多次 unlock 解锁。
4.PTHREAD_MUTEX_DEFAULT 适应锁,动作最简单的锁类型,仅等待解锁后重新竞争,没有等待队列。

// 常用的一些方法
int pthread_mutex_lock(pthread_mutex_t *);
int pthread_mutex_trylock(pthread_mutex_t *);
int pthread_mutex_unlock(pthread_mutex_t *);
int pthread_mutex_destroy(pthread_mutex_t *);
int pthread_mutex_setprioceiling(pthread_mutex_t * __restrict, int,int * __restrict);
int pthread_mutex_getprioceiling(const pthread_mutex_t * __restrict,int * __restrict);

如何利用 pthread_mutex 创建一个递归锁
static pthread_mutex_t theLock;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&theLock, &attr);

15、分步锁 - NSDistributedLock
在引用计数表的数据结构里,一张sideTable表利用分离锁被分成了多个部分
这样可以对一张表的多个部分,同时进行操作,提升了效率。

16、如果确保线程安全
1、采用 串行队列
2、加上同步锁

17、NSMutableArray 和NSMultableDictionary是线程安全的吗?NSCache那?
在做缓存时,有限使用NSCache 而不是 NSDictionary,我们熟悉的框架SDWebImage就是采用NSCache
NSCache优点:
1、系统资源将要耗尽时,可以自动删减缓存
2、可以设置最大缓存数量
3、可以设置最大占用内存值
4、NSCache 线程是安全的

18、GCD与NSOperationEQueue有哪些异同
1、GCD是纯C的API,NSOperationQueue 是基于GCD的封装
2、GCD只支持FIFO队列 ,NSOpeartionQueue可以方便设置执行顺序,设置最大的并发数
3、NSOperationQueue可以方便的设置operation 之间的依赖关系,GCD则需要更多的代码
4、NSOperationQueue支持KVO,可以检测operation是否正在执行(isExecuted),是否结束(isFinished),是否取消(is Canceled)
5、GCD的执行速度比NSOperationQueue快

使用场景:
任务之间不太相互依赖:GCD
任务之间有依赖或要监听任务的执行情况L:NSOperationQueue

19、解释一下多线程中的死锁?
死锁是由于多线程(进程)在执行过程中,因为争夺资源而造成的互相等待现象,你可以理解为卡住了。产生死锁的必要条件有四个:
1、互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源由一个进程占用。如果此时还有其他进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
2、请求和保持条件:指进程已保持至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程组塞,但又对自己已获得的其他资源保持不放
3、不可剥夺条件:指进程已获得的资源,在为使用完之前,不能被剥夺,只能在使用完时由自己释放
4、环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{p0,p1,p2,...,pn}中的p0正在等待一个p1占用的资源;p1正在等待p2占用的资源。。。。

dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"2");
    });
    
NSLog(@"1");

最常见的就是同步函数+主队列的组合,本质就是队列组塞

20、

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

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,101评论 1 32
  • 1、内存布局 stack:方法调用 heap:通过alloc等分配对象 bss:未初始化的全局变量等。 data:...
    AKyS佐毅阅读 1,596评论 0 19
  • 内存布局 stack(栈区): 方法调用 heap(堆区):通过alloc等分配的对象 bss:未初始化的全局变量...
    Jimmy_L_Wang阅读 621评论 1 3
  • iOS中内存管理机制是开发中一项很重要的知识,了解iOS中内存管理的规则不管是在开发中还是在学习中都能很大程度的帮...
    Mr_Atom阅读 3,410评论 1 4
  • 文|桥豆麻袋 审校|羊羊羊 趁着清明节小假期去济南浪了一圈,主要是我那帮闺蜜有一年没见了,就在北京、上海、日照、芜...
    小袋电影阅读 309评论 0 1