面试题小集

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 

// 定义一个链表
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  {
@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  {
@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、

你可能感兴趣的:(面试题小集)