iOS面试

2017年最新 iOS面试题及答案

2017-11-15  本文已影响114人  騂跃神话
 // retain
- (void)setName:(NSString *)str { 
    [str retain];
    [_name release];
    _name = str;
 } 
// copy
- (void)setName:(NSString *)str {
     id t = [str copy];
     [_name release];
     _name = t; 
}

@public 任何地方都能访问; @protected 该类和子类中访问,是默认的; @private 只能在本类中访问; @package 本包内使用,跨包不可以。

@implementation Son : Father

- (id)init { 
  if (self = [super init]) { 
    NSLog(@"%@", NSStringFromClass([self class]));
 // Son
    NSLog(@"%@", NSStringFromClass([super class]));
 // Son
   } 
  return self;
 } 
@end 
// 解析: self 是类的隐藏参数,指向当前调用方法的这个类的实例。 super是一个Magic Keyword,
它本质是一个编译器标示符,和self是指向的同一个消息接收者。 不同的是:super会告诉编译器,
调用class这个方法时,要去父类的方法,而不是本类里的。 
上面的例子不管调用[self class]还是[super class],接受消息的对象都是当前 Son *obj 这个对象。
// 创建 
@protocol MyDelagate 
@required 
-(void)eat:(NSString *)foodName; 
@optional 
-(void)run; 
@end

// 声明 .h 
@interface person: NSObject <MyDelegate>

@end

// 实现 .m 
@implementation person 
- (void)eat:(NSString *)foodName { 
  NSLog(@"吃:%@!", foodName); 
}
- (void)run { 
  NSLog(@"run!"); 
}
@end
// OneViewController.m 
TwoViewController twoVC = [[TwoViewController alloc] init]; 
twoVC.valueBlcok = ^(NSString str) { 
  NSLog(@"OneViewController拿到值:%@", str); 
}; 
[self presentViewController:twoVC animated:YES completion:nil];

// TwoViewController.h (在.h文件中声明一个block属性) 
@property (nonatomic ,strong) void(^valueBlcok)(NSString *str);

// TwoViewController.m (在.m文件中实现方法)
- (void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event { 
// 传值:调用block 
  if (_valueBlcok) { 
    _valueBlcok(@"123456"); 
  } 
}

block的注意点
1). 在block内部使用外部指针且会造成循环引用情况下,需要用week修饰外部指针:
_ _weak typeof(self) weakSelf = self;
2). 在block内部如果调用了延时函数还使用弱指针会取不到该指针,因为已经被销毁了,需要在block内部再将弱指针重新强引用一下。 _ _strong typeof(self) strongSelf = weakSelf;
3). 如果需要在block内部改变外部栈区变量的话,需要在用_ _block修饰外部变量。

// 创建队列组 
dispatch_group_t group = dispatch_group_create(); 
// 获取全局并发队列 
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
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(), ^{ // 合并图片 });
// 1.创建并发队列 
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT); 
// 2.向队列中添加任务 
dispatch_async(queue, ^{
 // 1.2是并行的 
  NSLog(@"任务1, %@",[NSThread currentThread]); 
}); 
dispatch_async(queue, ^{ 
  NSLog(@"任务2, %@",[NSThread currentThread]); 
});
dispatch_barrier_async(queue, ^{ 
  NSLog(@"任务 barrier, %@", [NSThread currentThread]); 
});
dispatch_async(queue, ^{ 
// 这两个是同时执行的 
  NSLog(@"任务3, %@",[NSThread currentThread]); 
}); 
dispatch_async(queue, ^{ 
  NSLog(@"任务4, %@",[NSThread currentThread]); 
});

// 输出结果: 任务1 任务2 ----> 任务 barrier ---->任务3 任务4 
// 其中的任务1与任务2,任务3与任务4 由于是并行处理先后顺序不定。
- (void)viewDidLoad {
  [super viewDidLoad]; 
  NSLog(@"1"); 
  dispatch_sync(dispatch_get_main_queue(), ^{ 
    NSLog(@"2"); 
  }); 
  NSLog(@"3"); 
} // 只输出:1。(主线程死锁)
int main(int argc, char * argv[]) { 
  @autoreleasepool { 
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 
  } 
}
// 创建线程的方法
[NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil]
[self performSelectorInBackground:nil withObject:nil];
[[NSThread alloc] initWithTarget:nil selector:nil object:nil];
dispatch_async(dispatch_get_global_queue(0, 0), ^{});
[[NSOperationQueue new] addOperation:nil];

// 主线程中执行代码的方法
[self performSelectorOnMainThread:nil withObject:nil waitUntilDone:YES];
dispatch_async(dispatch_get_main_queue(), ^{});
[[NSOperationQueue mainQueue] addOperation:nil];
static id _instance;
- (id)allocWithZone:(struct _NSZone *)zone { 
  static dispatch_once_t onceToken; 
  dispatch_once(&onceToken, ^{
     _instance = [super allocWithZone:zone]; 
  }); 
  return _instance; 
}

- (instancetype)sharedData { 
  static dispatch_once_t onceToken; 
  dispatch_once(&onceToken, ^{ 
    _instance = [[self alloc] init]; 
  }); 
  return _instance; 
}

- (id)copyWithZone:(NSZone *)zone { 
  return _instance; 
}
NSString str = @"abc123"; 
NSArray arr = [str componentsSeparatedByString:@""];
//以目标字符串把原字符串分割成两部分,存到数组中。@[@"abc", @"123"];
- (UIImage *)circleImage { 
// NO代表透明 
  UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0); 
// 获得上下文 
  CGContextRef ctx = UIGraphicsGetCurrentContext(); 
// 添加一个圆 
  CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);     
  CGContextAddEllipseInRect(ctx, rect); 
// 裁剪 
  CGContextClip(ctx); 
// 将图片画上去 
  [self drawInRect:rect]; 
  UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 
// 关闭上下文 
  UIGraphicsEndImageContext(); 
  return image; 
}

还有一种方案:使用了贝塞尔曲线"切割"个这个图片, 给UIImageView 添加了的圆角,其实也是通过绘图技术来实现的。

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; 
imageView.center = CGPointMake(200, 300); 
UIImage *anotherImage = [UIImage imageNamed:@"image"]; 
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0); 
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:50] addClip]; 
[anotherImage drawInRect:imageView.bounds]; 
imageView.image = UIGraphicsGetImageFromCurrentImageContext(); 
UIGraphicsEndImageContext(); 
[self.view addSubview:imageView];
/**
纯代码初始化控件时一定会走这个方法 
*/
- (instancetype)initWithFrame:(CGRect)frame { 
  if(self = [super initWithFrame:frame]) { 
    [self setupUI]; 
  } 
  return self; 
}
/**
通过xib初始化控件时一定会走这个方法 
*/
//加载xib第一步走这儿
- (instancetype)initWithCoder:(NSCoder *)aDecoder { 
  if(self = [super initWithCoder:aDecoder]) { 
  //单独在这儿调用[self setupUI];而不在awakeFromNib里调用,clearButton的enabled状态不能体现出来
    [self setupUI]; 
  } 
  return self; 
}
//加载xib第二步走这儿
-(void)awakeFromNib {
    [super awakeFromNib];
    [self setupUI];
}
- (void)setupUI { 
  // 初始化代码 
  for (UIView *stackView in self.subviews) {
          if ([stackView isKindOfClass:[UIStackView class]]) {
              for (UIButton *btn in stackView.subviews) {
                  if ( btn.tag == 1003 ) {
                      btn.selected = YES;
                      self.currentSelectBtn = btn;
                  }
              }
          }
      }
    
      self.clearButton.enabled = NO;
}

/* 1.直接遍历法 */

int maxCommonDivisor(int a, int b) { 
  int max = 0; 
  for (int i = 1; i <=b; i++) { 
    if (a % i == 0 && b % i == 0) { 
      max = i; 
    } 
  } 
  return max; 
}

/* 2.辗转相除法*/

int maxCommonDivisor(int a, int b) { 
  int r; 
  while(a % b > 0) {
    r = a % b; 
    a = b; 
    b = r; 
  } 
  return b; 
}

// 扩展:最小公倍数 = (a * b)/最大公约数

/**
栈是一种数据结构,特点:先进后出
练习:使用全局变量模拟栈的操作 
*/
include
include
include
//保护全局变量:在全局变量前加static后,这个全局变量就只能在本文件中使用 
static int data[1024];
//栈最多能保存1024个数据 
static int count = 0;
//目前已经放了多少个数(相当于栈顶位置)
//数据入栈 push 
void push(int x){ 
  assert(!full());
  //防止数组越界 
  data[count++] = x; 
} 
//数据出栈 pop 
int pop(){ 
  assert(!empty()); 
  return data[--count]; 
} 
//查看栈顶元素 top 
int top(){ 
  assert(!empty()); 
  return data[count-1]; 
}
//查询栈满 full 
bool full() { 
  if(count >= 1024) { 
    return 1; 
  } 
  return 0; 
}
//查询栈空 empty 
bool empty() { 
  if(count <= 0) { 
    return 1; 
  } 
  return 0; 
}

int main(){ 
  //入栈 
  for (int i = 1; i <= 10; i++) { 
    push(i); 
  }
  //出栈
  while(!empty()){
      printf("%d ", top()); //栈顶元素
      pop(); //出栈
  }
  printf("\n");
  return 0;
}

选择排序

/**
【选择排序】:最值出现在起始端
第1趟:在n个数中找到最小(大)数与第一个数交换位置
第2趟:在剩下n-1个数中找到最小(大)数与第二个数交换位置
重复这样的操作...依次与第三个、第四个...数交换位置
第n-1趟,最终可实现数据的升序(降序)排列。
*/ 
void selectSort(int arr, int length) { 
  for (int i = 0; i < length; xss=removed> arr[j]) { 
    int temp = arr[i]; 
    arr[i] = arr[j]; 
    arr[j] = temp; 
  } 
} } }

冒泡排序

/**
【冒泡排序】:相邻元素两两比较,比较完一趟,最值出现在末尾
第1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第n个元素位置
第2趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第n-1个元素位置
…… ……
第n-1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第2个元素位置 */ 
void bublleSort(int arr, int length) { 
  for(int i = 0; i < length xss=removed> arr[j+1]) { 
  int temp = arr[j]; 
  arr[j] = arr[j+1]; 
  arr[j+1] = temp; 
  } 
} } }

折半查找(二分查找)

/**
折半查找:优化查找时间(不用遍历全部数据)
折半查找的原理:
1> 数组必须是有序的
2> 必须已知min和max(知道范围)
3> 动态计算mid的值,取出mid对应的值进行比较
4> 如果mid对应的值大于要查找的值,那么max要变小为mid-1
5> 如果mid对应的值小于要查找的值,那么min要变大为mid+1
*/

// 已知一个有序数组, 和一个key, 要求从数组中找到key对应的索引位置 
int findKey(int *arr, int length, int key) { 
  int min = 0, max = length - 1, mid; 
  while (min <= max) { 
  mid = (min + max) / 2; 
  //计算中间值 
  if (key > arr[mid]) { 
    min = mid + 1; 
  } else if (key < arr xss=removed>
@interface An_Object : NSObject
@property (nonatomic, readonly, copy) NSString *name; 
@property (nonatomic, readonly, assign) NSUInteger age; 
@property (nonatomic, readonly, assign) BRUserGender gender;

- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age gender:(BRUserGender)gender;
@end

//说明: 
//既然该类中已经有一个“初始化方法” ,用于设置 name、age 和 gender 的初始值: 
那么在设计对应 @property 时就应该尽量使用不可变的对象:
其三个属性都应该设为“只读”。用初始化方法设置好属性值之后,就不能再改变了。
//属性的参数应该按照下面的顺序排列: (原子性,读写,内存管理)

参考:2017年最新 iOS面试题及答案

上一篇下一篇

猜你喜欢

热点阅读