iOS 面试题(一)

2022-03-09  本文已影响0人  搬砖的crystal

1.instancetypeid的区别

区别一:
区别二:

2. @synthesize@dynamic区别

在声明property属性后,有2种实现选择:

@synthesize

编译器期间,让编译器自动生成getter/setter方法。
当有自定义的存或取方法时,自定义会屏蔽自动生成该方法。

@dynamic

告诉编译器,不自动生成getter/setter方法,避免编译期间产生警告,然后由自己实现存取方法。
或存取方法在运行时动态创建绑定:主要使用在CoreData的实现NSManagedObject子类时使用,由Core Data框架在程序运行的时动态生成子类属性。

同时重写getter/setter方法

很少同时重写getter/setter方法,一般的话,大概都是使用懒加载方法,然后重写getter方法,做一个非空判断。要同时重写属性的getter/setter,系统就会报错误:Use of undeclared identifier '_string'; did you mean 'string'?

OC最初设定@property@synthesize的作用:
@property的作用是定义属性,声明getter/setter方法。(注意:属性不是变量)
@synthesize的作用是实现属性的,如getter/setter方法。

后来因为使用@property灰常频繁,就简略了@synthesize的表达。
从Xcode4.4以后@property已经独揽了@synthesize的功能。
主要有三个作用:
(1)生成了私有的带下划线的的成员变量因此子类不可以直接访问,但是可以通过getter/setter方法访问。那么如果想让定义的成员变量让子类直接访问那么只能在.h文件中定义成员变量了,因为它默认是@protected
(2)生成了getter/setter方法的实现当:用@property声明的成员属性,相当于自动生成了getter/setter方法,如果重写了getter/setter方法,与@property声明的成员属性就不是一个成员属性了,是另外一个实例变量,而这个实例变量需要手动声明。所以会报错误。

总结:一定要分清属性和变量的区别,不能混淆。@synthesize声明的属性=变量。意思是,将属性的getter/setter方法,作用于这个变量。

同时重写解决办法如下:

@interface ViewController ()
@property (nonatomic, strong)NSString *string;
@end
@implementation ViewController

//添加以下代码解决
@synthesize string = _string;

-(NSString *)string{
    return _string;
}

-(void)setString:(NSString *)string{
    _string = string;
}

-(void)viewDidLoad{
    [super viewDidLoad];
}

@end

3.NSProxy和NSObject

https://www.jianshu.com/p/0c31fed0a27a

4.传值通知和推送通知(本地&远程)

https://www.jianshu.com/p/d5011177ba90

5.imageNameImageWithContextOfFile

6.NSCacheNSDcitionary

NSCache使用很方便,提供了类似可变字典的实现方式,但它比可变字典更适用于实现缓存。

#import "DJViewController.h"
@interface DJViewController ()<NSCacheDelegate>

@end

@implementation DJViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    NSCache *cache = [[NSCache alloc]init];
    //最大可缓存对象的个数为5个
    [cache setCountLimit:5];
    cache.delegate = self;
    
    NSString *test = @"Hello World!";
    [cache setObject:test forKey:@"Test"];
    
    for (int i = 0; i < 10; i++) {
        [cache setObject:[NSString stringWithFormat:@"world__%d",i] forKey:[NSString stringWithFormat:@"hello__%d",i]];
        NSLog(@"add %d",i);
    }
    
    for (int i = 0; i < 10; i++) {
        NSLog(@"get %d,%@",i,[cache objectForKey:[NSString stringWithFormat:@"hello__%d",i]]);
    }
    
    [cache removeAllObjects];
    
    for (int i = 0; i < 10; i++) {
        NSLog(@"get %d,%@",i,[cache objectForKey:[NSString stringWithFormat:@"hello__%d",i]]);
    }
    
}

//当缓存的对象即将被删除时调用此方法
-(void)cache:(NSCache *)cache willEvictObject:(id)obj{
    NSLog(@"remove %@",obj);
}

@end
//输出结果
2021-09-22 15:33:30.829312+0800 DJTestDemo[71357:240307] add 0
2021-09-22 15:33:30.829445+0800 DJTestDemo[71357:240307] add 1
2021-09-22 15:33:30.829540+0800 DJTestDemo[71357:240307] add 2
2021-09-22 15:33:30.829621+0800 DJTestDemo[71357:240307] add 3
2021-09-22 15:33:30.829729+0800 DJTestDemo[71357:240307] remove Hello World!
2021-09-22 15:33:30.829800+0800 DJTestDemo[71357:240307] add 4
2021-09-22 15:33:30.829900+0800 DJTestDemo[71357:240307] remove world__0
2021-09-22 15:33:30.829972+0800 DJTestDemo[71357:240307] add 5
2021-09-22 15:33:30.830055+0800 DJTestDemo[71357:240307] remove world__1
2021-09-22 15:33:30.830181+0800 DJTestDemo[71357:240307] add 6
2021-09-22 15:33:30.830484+0800 DJTestDemo[71357:240307] remove world__2
2021-09-22 15:33:30.830697+0800 DJTestDemo[71357:240307] add 7
2021-09-22 15:33:30.830922+0800 DJTestDemo[71357:240307] remove world__3
2021-09-22 15:33:30.831102+0800 DJTestDemo[71357:240307] add 8
2021-09-22 15:33:30.831310+0800 DJTestDemo[71357:240307] remove world__4
2021-09-22 15:33:30.831471+0800 DJTestDemo[71357:240307] add 9
2021-09-22 15:33:30.831675+0800 DJTestDemo[71357:240307] get 0,(null)
2021-09-22 15:33:30.842161+0800 DJTestDemo[71357:240307] get 1,(null)
2021-09-22 15:33:30.842600+0800 DJTestDemo[71357:240307] get 2,(null)
2021-09-22 15:33:30.842833+0800 DJTestDemo[71357:240307] get 3,(null)
2021-09-22 15:33:30.843000+0800 DJTestDemo[71357:240307] get 4,(null)
2021-09-22 15:33:30.843301+0800 DJTestDemo[71357:240307] get 5,world__5
2021-09-22 15:33:30.843423+0800 DJTestDemo[71357:240307] get 6,world__6
2021-09-22 15:33:30.843517+0800 DJTestDemo[71357:240307] get 7,world__7
2021-09-22 15:33:30.843688+0800 DJTestDemo[71357:240307] get 8,world__8
2021-09-22 15:33:30.844061+0800 DJTestDemo[71357:240307] get 9,world__9
2021-09-22 15:33:30.844323+0800 DJTestDemo[71357:240307] remove world__6
2021-09-22 15:33:30.844563+0800 DJTestDemo[71357:240307] remove world__7
2021-09-22 15:33:30.844952+0800 DJTestDemo[71357:240307] remove world__8
2021-09-22 15:33:30.845037+0800 DJTestDemo[71357:240307] remove world__9
2021-09-22 15:33:30.845546+0800 DJTestDemo[71357:240307] remove world__5
2021-09-22 15:33:30.845637+0800 DJTestDemo[71357:240307] get 0,(null)
2021-09-22 15:33:30.845800+0800 DJTestDemo[71357:240307] get 1,(null)
2021-09-22 15:33:30.846037+0800 DJTestDemo[71357:240307] get 2,(null)
2021-09-22 15:33:30.846305+0800 DJTestDemo[71357:240307] get 3,(null)
2021-09-22 15:33:30.846620+0800 DJTestDemo[71357:240307] get 4,(null)
2021-09-22 15:33:30.846852+0800 DJTestDemo[71357:240307] get 5,(null)
2021-09-22 15:33:30.847095+0800 DJTestDemo[71357:240307] get 6,(null)
2021-09-22 15:33:30.847342+0800 DJTestDemo[71357:240307] get 7,(null)
2021-09-22 15:33:30.847673+0800 DJTestDemo[71357:240307] get 8,(null)
2021-09-22 15:33:30.847882+0800 DJTestDemo[71357:240307] get 9,(null)

从输出可以看出,当我们要添加第六个对象时NSCache自动删除了我们添加的第一个对象并触发了NSCacheDelegate的回调方法,添加第七个时也是同样的,删除了缓存中的一个对象才能添加进去。

在第二个for循环中,我们通过key取出所有的缓存对象,前五个对象取出都为nil,因为在添加后面的对象时前面的被删除了,所以,当我们从缓存中获取对象时一定要判断是否为空,我们无法保证缓存中的某个对象不会被删除。

7.setNeedsDisplaysetNeedsLayout方法

https://www.jianshu.com/p/b296e03429d4

8. UILayerUiView

https://www.jianshu.com/p/708bee808dfc

9.layoutSubViewsdrawRect

layoutSubviews调用时机
drawRect:调用机制
drawRect:方法使用注意点:

10.CPU和GPU

https://www.jianshu.com/p/412d7cd68756

11.点(pt)和像素(px)

px:pixel像素,屏幕上显示的最小单位。在同一个屏幕尺寸,更高的PPI(pixel per inch像素密度,指每英尺的像素数),就能显示更多的像素,渲染的内容会更清晰。
pt:point点,是一个标准的长度单位,与分辨率无关,1pt=1/72inch。根据屏幕像素的密度,一个点可以包含多个像素。例如,在标准的Retina显示屏上1pt里有2*2个px。

12.intNSInteger的区别

NSInteger是一个封装,它会识别当前操作系统的位数,自动返回最大的类型。

13.#import#include

在C和C++里是没有#import的,只有#include用来包含头文件。#include就是将目标.h文件中的内容拷贝到当前文件中,并替换掉这句include。

但是这样做可能会因为重复引用带来编译错误,比如B和C都引用了A,D又同时引用了B和C,这样D引用了A两次。为了解决这个问题,OC加入了#import,就是为了使得头文件只被引用一次。

当引用关系很复杂时,编译引用所占的代码量就会大幅上升,因为被引用的头文件在引用的地方都被拷贝了一次。
为了解决这个问题,C语言引入了预编译头文件(PreCompiled Header),将公用的头文件放入预编译头文件中预先进行编译,然后在真正编译工程时再将预先编译好的产物加入到所有待编译的Source中去,来加快编译速度。

@import

关于 @import是iOS 7之后的新特性语法,这种方式叫Modules(模块导入) 或者 "semantic import(语义导入)" ,是一种更好的头部预处理的执行方式,这iOS 7之后你能通过@import语法来导入任何的framework,Modules是一种将所有可执行的framework打包在一起,貌似这种方式比起传统的#import更安全和更高效。
而且另外一个最大的改进就是使用@import之后,你不用在project settings那里添加framework,系统会自动帮你加载上了,方便了很多,也避免了很多不必要的错误,例如忘记了加入framework而出现的 "Linker Error"。

@class

在.h文件进用@class来声明,只告诉编译器,声明的类的名称。避免循环引用头文件。

14.全局和静态变量

static

(1)static修饰局部变量
局部变量:在函数/方法/代码块内声明的变量。它的生命周期、作用域都是在这个代码块内。存储在栈区(stack)一旦出了这个代码块,存储局部变量的这个栈内存就会被回收,局部变量也就被销毁。

当用static修饰局部变量时,变量被称为静态局部变量,和全局变量,静态全局变量一样,是存储在静态存储区。存储在静态存储区的变量,其内存直到 程序结束 才会被销毁。即,生命周期是整个源程序。
(2)static修饰全局变量

const

主要强调变量是不可修改的。
const修饰的是其右边的值,也就是const右边的这个整体的值不能改变。

const*前:

const修饰*str这个整体,所以这个整体不能改变,这个整体是str指向的内存中的值。

//值不变
static NSString const *name = @"abc";
static const NSString *name = @"bac";   //两种写法等价

// name是 指针变量, *name是 指针指向的变量的值
const*后:

表示str指向的地址不能改变。

NSString * const str = @"abc";    // 地址不变
extern

主要是用来引用全局变量,它的原理是先在本文件中查找,查找不到再到其他文件中查找。

//.h中
@interface PDConst : NSObject

extern NSString *const appBaseURL;

@end
//.m中
@implementation PDConst

NSString *const currentBaseURL = @"http://192....";

@end

15.类

https://www.jianshu.com/p/40221ef8128e

16.字符串比较

OC中比较两个字符串是否相等时,应该用isEqualToString:而不能仅仅只是比较字符串的指针值。

//比较内容
NSString *str1=@"hello 1";  
NSString *str2;  
str2=[NSString stringWithFormat:@"hello %d", 1];  
if ([thing1 isEqualToString:thing2]){  
   NSLog(@"They are the same!")  
}  
//比较指针地址
if (str1==str2){  
   NSLog(@"They are the same!")  
} 

这是因为==运算符只判断str1和str2的指针数值,而不是他们所指的对象。由于str1,str2是不同的字符串,所以第二种比较方式会认为它们是不同的。
有时我们想检查两个对象的标识:str1和str2是同一个对象吗?这时就应该使用运算符==。如果是想查看等价性(即这两个字符串是否代表同一个事物吗),那么请使用isEqualToString:

17.NSNumberNSValue

作用

由于集合里只能存放对象,不可以存放基本数据类型,所以我们有时候需要讲一些对象比如基本数据类型,结构体等存到NSDictionaryNSArray中,我们就需要将这些数据类型或结构体包装成OC对象,以便集合能访问到。常用的用来包装这些类型的有NSNumberNSValue

区别

NSNumber

//包装
int age = 20;
NSNumber *num = [NSNumber numberWithInt:age];//将基本数据类型int对象age 包装成NSNumber对象
@(age);//直接包装

//拆封
[num intValue];
NSValue
将结构体包装成OC对象

CGPoint p = CGPointMake(1,2);
NSValue *val = [NSValue valueWithPoint:p];//将结构体p包装成NSValue对象

//拆
[value pointValue]

18.nilNilNull[NSNull null]

nil就是空对象。把一个对象置成nil之后,就不能对其进行retaincopy等引用计数相关的操作了。
在iOS中,Nil完全等同于nil
NUll就是C语言中的一个空指针,在Objective-C中也可以使用。
[NSNull null]是值为空的对象。

空对象和值为空的对象的区别:
“空对象”是已经释放了内存地址的对象,即不存在的对象。
“值为空的对象”是分配了地址,但是没有值得对象,是实际存在的对象。

19.沙盒

(1)应用程序沙盒目录
(2)获取目录的方法
//Home目录
NSString *homeDirectory = NSHomeDirectory();

//Document目录   documents (Documents)
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *path = [paths objectAtIndex:0];

//Libaray目录  various documentation, support, and configuration files, resources (Library)
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask,YES);
NSString *path = [paths objectAtIndex:0];

//Cache目录  location of discardable cache files (Library/Caches)
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES);
NSString *path = [paths objectAtIndex:0];

20.loadViewviewDidLoad

(1)loadView
作用

每次访问UIViewController的view(比如controller.viewself.view)而且view为nil,loadView方法就会被调用。loadView方法是用来负责创建UIViewController的view。

默认实现

默认实现即[super loadView]里面做了什么事情。
1)它会先去查找与UIViewController相关联的xib文件,通过加载xib文件来创建UIViewController的view。
如果在初始化UIViewController指定了xib文件名,就会根据传入的xib文件名加载对应的xib文件。

[[MJViewController alloc] initWithNibName:@"MJViewController" bundle:nil];

如果没有明显地传xib文件名,就会加载跟UIViewController同名的xib文件。
2)如果没有找到相关联的xib文件,就会创建一个空白的UIView,然后赋值给UIViewController的view属性。

使用

大家都知道UIViewController的view可以通过xib文件来创建,但是在某些情况下,xib不是那么地灵活,所以有时候我们想通过代码来创建UIView。 如果想通过代码来创建UIViewController的view,就要重写loadView方法,并且不需要调用[super loadView],因为在第3点里面已经提到:若没有xib文件,[super loadView]默认会创建一个空白的UIView。我们既然要通过代码来自定义UIView,那么就没必要事先创建一个空白的UIView,以节省不必要的开销。

(2)viewDidLoad

不管是通过xib文件还是重写loadView创建UIViewController的view,在view创建完毕后,最终都会调用viewDidLoad方法。
一般我们会在这里做界面上的初始化操作,比如往view中添加一些子视图、从数据库或者网络加载模型数据装配到子视图中。

上一篇下一篇

猜你喜欢

热点阅读