iOS基础知识点一

175 阅读21分钟

1.OC 的多态

在面向对象编程中,多态是指在不同的对象之间存在着同名的方法和属性,但是具体实现不同,因此使用这些对象的时候会变现出不同的行为.在OC中,多态可以通过继承和协议来实现.

  • 1.继承实现多态

在oc中,子类可以继承父类的属性和方法.如果子类重新定义了父类的某个方法,那么当调用这个方法时,实际上调用的是子类的方法而不是父类的方法.这就是多态的一种表现. 例如:

@interface Animal:NSObject
- (void)say;
@end

@implementation Animal

- (void)say{
  NSLog(@"I am an animal.")
}
@end

@interface Cat:Animal
@end

@implementation Cat
- (void)say{
    NSLog(@"I am an cat.")
}
@end

在上面的例子中,Animal 类有一个say 方法,输出"I am an animal.". Cat类继承Animal类,并且重新定义了say方法,输出"I am an cat."当我们创建一个Cat对象并调用say方法时,会输出"I am an cat."这就是一种多态.

  • 2.协议实现多态

OC中协议类似于Java中的接口,用于定义一组方法或属性的集合.不同的类可以遵循同一个协议,并且实现协议中的方法,从而实现多态.

@protocol Shape<NSObject>
- (CGFloat)area;
@end

@interface Rectangle:NSObject <Shape>

@property(nonatomic,assign)CGFloat width;
@property(nonatomic,assign)CGFloat height;

@end

@implementation Rectangle

- (CGFloat)area{
    return self.width * self.height;
}

@end

@interface Circle :NSOjct <Shape>
@property(nonation,assign)CGFloat radius;
@end

@implementation Circle
- (CGFloat)area{
    return M_PI *self.radius * self.radius;
}
@end

在上面的例子中,shape 是一个协议,定义了一个area方法.Rectangle类和Circle类都实现了Shape协议,并且实现了area 方法.当我们需要计算一个形状的面积时,可以先定义一个id类型的变量,然后把Rectangle 对象或者Circle对象赋值给它,然后调用area方法,无论是哪种形状,都会调用自己的area 方法,这就是多态的一种体现.

总之,OC的多态可以通过继承和协议来实现,能够提高代码的可拓展性和可维护性.

2.Java、Python、OC运行效率比较

Java、Python、Objct-C这三种语言的效率可以从多个方面进行比较:

  • 1.编译型VS解释型:Java 是一种编译型语言,需要先将代码编译成字节码再运行,这样可以提高代码提高效率;Python 和 Objct-C 是解释型语言,代码在运行时被解释器逐行解释执行,这种方式相对比较慢,因此Python 和 Object-C的运行效率比Java低.

  • 2.内存管理:Java和Object-C都采用了垃圾回收机制,可以自动管理内存,这样可以减少内存泄漏等问题,但是也会影响运行效率;Python 也有垃圾回收机制,但是Python的垃圾回收机制相对比较简单,因此Python对于内存管理效率可能不如Java和Objct-C

  • 3.库的支持:Java和Python 都有非常丰富的库支持,这些库可以提供很多常用的功能,例如网络通信、数据库访问、图像处理等.Objct-C 在macOS和iOS系统上有很好的支持,可以很方便的调用系统的API,但是在其他领域可能比较欠缺.

  • 4.并发处理:Java对于并发处理的支付非常好,具有多线程和并发编程的优势,可以很好的处理高并发场景;Python也有一些多线程和异步编程的库,但是在处理高并发的场景时可能效率不如Java.Object-C对于并发处理的支持相对较弱,但是在macOS 和iOS系统上可以使用Grand Central Dispatch(GCD)等技术来实现并发编程.

综上所述,Java的运行效率相对较高,特别是在高并发场景下有很好的表现;Python 在处理科学计算、机器学习等领域广泛应用,但是相对比较慢;Objct-C在macOS和iOS系统上有很好的支持,但是在其他领域可能欠缺.不过,这些语言的运行效率受到多种因素的影响,具体情况还需要根据具体应用场景来进行选择.

3.内存管理

在iOS中,内存管理是非常重要的一部分,因为不合理的内存管理可能会导致应用程序崩溃或者性能下降. Objct-C是一种基于引用计数的内存管理模型.在Objct-C中,每个对象都有一个引用计数,当一个对象被创建时,引用计数的值为1,当有其他的对象引用该对象时,引用计数器的值会+1,当没有任何对象引用该对象时,引用计数的值会-1.当引用计数器的值为0时,该对象会被释放,内存被回收.

在Objct-C中,开发者需要手动管理内存,急需要调用retain和release方法来增加或减少引用计数器的值.如果引用计数器的值不正确,会导致内存泄露或野指针等问题.

  1. 野指针:指针变量指向了已经被释放或者未初始化的内存区域
  2. 内存泄露:申请的内存空间没有被正确释放,导致程序持续占用内存资源,会导致程序崩溃或者系统变慢问题.

为了方便内存管理,iOS中提供了自动引用计数(ARC)的机制.ARC可以自动的为开发者处理引用计数的增加或者减少,从而减少了程序员需要编写的代码量和内存管理的出错率.开发只需要关注一下基本规则,比如在引用对象时使用若引用(weak refreshce)、避免循环引用等,就可以使用ARC进行内存管理.

总的来说,在iOS中,开发者需要理解引用计数的概念,并且掌握如何手动管理内存或使用自动引用计数机制来管理内存.正确的内存管理可以提高应用程序的稳定性和性能.

4.内存的几大区域

在iOS系统中,内存被划分为多个不同的区域,主要包括以下几个区域:

  1. 栈区(stack):用于存储函数的参数值、局部变量等.栈区的内存空间由系统自动管理,内存的申请和释放都是由编译器自动生成的指令来实现的.

  2. 堆区(Heap):用于存储动态分配的内存空间,例如使用 malloc()、calloc()、realloc()、等函数动态分配的内存空间.堆区的内存空间由程序员手动管理,需要在不需要使用的时候动态释放内存空间,否则就会导致内存泄露的问题.

  3. BSS段(Block Started by Symbol):用于存储未初始化的全局变量和静态变量.BSS段中的变量会被系统初始化为0或者空指针.

  4. 数据段(Data):用于储存已初始化的全局变量和静态变量.数据段中的变量会被系统初始化为指定的初值.

  5. 代码段(Code):用于存储程序的可执行代码.代码段中的内容只读,不允许进行写操作.

  6. 栈数据段(Stack Data):用于存储函数调用的临时数据,例如函数调用的返回地址、栈指针等.栈数据段是栈区的一部分,其内存空间由系统自动管理.

以上几个区域的大小和分配方式都是由操作系统自动管理和调用,因此在实际开发中,程序员无需过于关注这些细节.但是在使用动态分配内存等功能时,需要注意内存泄露的问题,并且及时释放不再使用的内存空间,以提高程序的稳定性和性能.

5.iOS 如何组件化解耦的

iOS组件化的目的是将一个大型应用程序拆分成多个独立的、可重用的组件,每个组件负责完成特定的功能.通过组件化,可以使应用程序代码更加模块化和灵活,便于开发和维护.

以下是iOS组件化解耦的一些常用技术:

  1. 通过URL路由实现组件的通信:每个组件都可以注册一个URL,并且通过URL路由器将URL映射到相应的组件.这样,在需要访问某个组件时,可以通过相应的URL访问组件,从而实现组件之间的解耦和通信.
// URL路由器实现代码,用于注册和映射URL
@interface URLRouter:NSObjct

+ (instancetype)sharedInstance;

- (void)map:(NSString *)route toHandler:(void (^)(NSDictionary *params))handlerBlock;
    
- (void)open:(NSString *)router;
    
// 每个组件的实现代码,用于注册和处理相应的URL
 @interface ComponentA:NSObjct
 +(void)load;
@end
    
@implementation ComponentA
+ (void)load{
    [URLRouter sharedInstance] map:@"componentA" toHandler:^(NSDictionary * params){
        //处理ComponentA的业务逻辑
    }]
 }
@end
    
// 调用组件的代码,用于通过URL路由器访问相应的组件:
    [[URLRouter sharedInstance] open:@"componentA"];
  1. 通过Protocol实现组件之间的解耦:将组件之间需要交互的协议抽象出来,定义为Protocol,并通过代理或者回调的方式将协议的实现交给其他组件去实现,从而实现组件之间的解耦.
// 定义协议
@protocol LoginProtocol <NSObjct>
@required
- (void)loginWithUserName:(NSString *)userName password:(NSString *)password;
@end

@interface loginManager:NSObject
@property(nonatamic,weak)id<LoginProtocol> delegate;
@end
    
@implementation LoginManager
- (void)login{
    [self.delegate loginWithUserName:@"userName" password:@"password"];
}    
@end
@interface:LoginComponent:NSObjct<LoginProtocol>
- (void)loginWithUserName:(NSString *)userName password:(NSString *)password{
    //处理登录逻辑
}
// 注册组件
[LoginManager sharedInstance].delegate = [LoginComponent new];
  1. 通过依赖注入(Depend Injection)实现组件之间的解耦:通过将组件的依赖关系抽象成接口,然后通过依赖注入的方式将实现注入到组件中,从而实现组件之间的解耦.
@interface LoginMananger:NSObjct
@property(nonatomic,strong) id<UserService> userService;
- (void)loginWithUserName:(NSString *)userName password:(NSString *)password;
@end
    
@implementation LoginManager
- (void)loginWithUserName:(NSString *)userName password:(NSString *)password{
    id<userInfo> userInfo = [self.userService getUserInfoWithUserName:userName];
}
@end
@protocol UserService <NSObjct>
- (id<UserInfo>)getUserInfoWithUserName:(NSString *)userName;
@end
    
@protocol UserInfo <NSObjct>
@property(nonatomic,copy)NSString *userName;
@property(nonatomic,copy)NSString *password;
@end
    
@interface UserServiceImpl :NSObjct <UserService>
    
@end
@implementation UserServiceImpl
- (id<UserInfo>)getUserInfoWithUserName:(NSString *)userName{
    //查询用户信息
    id<UserInfo> userInfo = [UserInfoImpl new];
    userInfo.userName = @"userName";
    userInfo.password = @"password";
    return userInfo;
}
@end
    
@interface UserInfoImpl: NSObjct <UserInfo>
    
@end
    
@implementation UserInfoImpl
    
@end
    
// 注册组件
[LoginManager sharedInstance].userService = [UserServiceImpl new];
  1. 通过通知中心(NSNotification) 实现组件之间的通信:组件通过注册和发送通知的方式进行通信,可以跨越不同组件和对象,实现组件之间的解耦.

  2. 通过Cocoapods管理组件依赖:Cocoapods 是iOS开发中常用的第三方管理工具,可以将不同的组件以库的形式进行管理,从而实现组件之间的解耦和独立开发.

在实际开发中,应根据具体的业务需求和项目规则选择适合的组件化解耦方案,并合理设计组件之间的接口和协议,以保证组件之间的交互更加清晰、高效和可维护.

6.如何令自己所写的对象具有拷贝功能?

要上自己所写的对象具有拷贝功能,可以通过实现对象的"clone()"方法来完成."clone()"方法会创建一个新的对象,并将原始对象的所有属性值复制到新的对象中.这个新的对象将是一个独立的副本,不会与原始对象共享任何引用.

以下是实现一个具有拷贝功能对象的示例代码:

public class MyClass implements Cloneable{
    private int intValue;
    private String stringValue;
    
    // 构造函数
    public MyClass(int intValue,String stringValue){
        this.intValue = intValue;
        this.stringValue = stringValue;
    }
    
   //getter 和 setter 方法
    public int getIntValue(){
        return intValue;
    }
    
    public void setIntValue(int intValue){
        this.intValue = intValue
    }
    
    public String getStringValue(){
        return stringValue;
    }
    
    public void setStringValue(String stringValue){
        this.stringValue = stringValue;
    }
    
    //实现 clone() 方法
    @Override
    public Objct clone() throws CloneNotSupportedException{
        return super.clone();
    }
}

在上面的代码中,我们首先实现了'MyClass'类,并在其中定义了'intValue'和'stringValue'两个属性.然后我们实现了一个构造函数和getter/setter方法.

最后,在'MyClass'类中实现了'Cloneable'接口,并覆盖了'clone()'方法.这里我们只是简单的调换用了了父类的'clone()'方法,以创建一个新的对象.如果需要深拷贝,则需要将对象中的所有属性都进行复制.

使用时,我们可以将下面这样创建对象进行拷贝:

MyClass obj1 = new MyClass(1,"hellow")
MyClass obj2 = (MyClass) objct1.clone()

这样就可以创建一个新的'MyClass'对象'Obj2',并将'Obj1'的所有属性值复制到'obj2'中,'obj2'和'obj1'是两个独立的对象,互不影响.

7.UIView和CALayer是什么关系?

UIView 和 CALayer 是iOS应用程序中两个重要的图形类,它们有着紧密的关系.UIView是屏幕上可视化元素的基本构建块,而CALayer则是UIView的基础.UIView通过管理一个或者多个CALayer对象两显示自己及其子视图.UIView可以有多个子视图,每个子视图都有自己的CALayer对象.

UIView和CALayer之间的关系可以通过以下方式表示:

UIView是iOS应用程序中的可视化元素,它有一些属性,例如背景色、大小、位置等.UIView本身并不直接绘制自己的内容,而是委托CALayer对象处理绘制任务.CALayer是iOS应用程序中的一个低级接口,它实际上绘制了UIView中可见的内容.CALayer对象由CoreAnimation框架管理,它具有绘制内容、动画和转换等功能.UIView可以调整自己的尺寸和位置,同时更新其底层的CALayer对象以反映这些更改.UIView还可以添加子视图和子图层,这些子视图和子图层也具有自己的CALayer对象.

下面是一些使用CALayer的示例代码:

1.使用CALayer显示一个红色矩形:

let layer = CALayer()
layer.from = CGRect(x:50,y:50,width:100,height:100)
layer.backgroundColor = UIColor.red.cgColor
view.layer.addSublayer(layer)

2.在UIView中添加一个子视图和一个子图层

let subview = UIView(frame:CGRect(x:50,y:50,width:100,height:100))
subview.backgroundColor = UIColor.blue
    
let sublayer = CALayer()
sublayer.frame = subview.bounds
sublayer.backgroundColor = UIColor.red.cgColor
    
subview.layer.addSublayer(sublayer)
view.addSubview(subview)

在这个示例中,我们创建了一个带有蓝色背景色的UIView,并向其添加了一个CALayer对象,该对象绘制了一个红色矩形.这个CALayer对象成为了UIView的子视图.接着,我们创建了另外一个UIView,这次使用了红背景,并在其上添加了一个红色的CALayer对象.这个UIView成为了原始UIView的子视图.

总的来说,UIView和CALayer都是重要的iOS图形类,它们紧密联系,互相协作,用于构建可视化界面和动画效果.

8.Runtime 如何通过selector 找到对应的IMP地址

在Objct-C中,方法调用是通过消息传递机制来实现的.当一个方法被调用时,实际上是通过给对象发送一个消息来实现的.这个消息包含了方法的选择器(Selector),也就是方法名.已经方法参数. 在运行时(runtime)中,当一个方法被调用时,首先会根据对象的类(Class)和方法的选择器(Selector)来查找方法的实现(IMP).这个过程会通过调用'objc_msgSend()'函数来实现的.这个函数的作用是根据对象的类和方法的选择器来查找方法的实现,并调用该实现. 具体的说,当'objct_msgSend()'函数被调用时,他会根据对象的类和方法的选择器,在类的方法列表中查找对应的方法.如果找到了对应的方法,就会返回该方法的实现(IMP)地址;如果没有找到对应的方法,则会在该类的父类中查找,直到找到为止了.如果最终还是没有找到对应的方法,则会抛出一个异常. 在查找方法的过程中,会先检查累的缓存(cache)中是否有对应的方法.如果有,则直接返回该方法的实现地址.如果缓存中没有对应的方法,则会根据方法选择器来查找方法.这个过程会通过调用'class_getMethodImplementation()'函数来实现的.这个函数的作用是根据类和方法选择器来查找方法的实现. 总之,当一个方法被调用时,运行时会根据对象的类和方法的选择器来查找方法的实现.这个过程是通过消息传递机制和'objc_msgSend()'函数来实现.在查找放的过程中,会先检查类的缓存中是否有对应的方法,然后在根据方法选择器来查找方法的实现.

9.Runloop的内部实现逻辑

Runloop是iOS开发中一个非常重要的概念,它负责管理应用程序事件的处理以及线程的生命周期.在iOS中,每个线程都有一个对应的runloop对象,它负责处理该线程的事件.

Runloop 的内部实现逻辑可以大致分为以下几个步骤:

  1. 获取当前线程的runloop对象:每个线程都有一个对应的runloop对象,可以通过'[NSRunloop currentRunLoop]'方法获取当前线程的runloop对象.
  2. 进入runloop循环:runloop的主要作用就是循环处理事件,因此,进入runloop循环是非常关键的一步.runloop提供了多个方法来启动runloop循环,比如:'run'和'runUntilDate:' 方法,这些方法会在没有事件需要处理时一直阻塞线程,直到有事件需要处理.
  3. 处理事件:当runloop循环启动后,runloop会不断地从时间队列中取出事件,并调用相应的事件处理方法.事件包括输入源事件(如触摸事件、鼠标事件等)、定时事件和自定义事件.
  4. 等待新事件的到来.如果当前事件队列中没有事件要处理,runloop就会进入休眠状态,等待新事件的到来.这样可以避免runloop一直占用线程资源,提高线程的利用率.
  5. 退出runloop循环.当runloop不再需要处理事件时,可以通过调用'CFRunLoopStop'方法来退出runloop循环. 总的来说,runloop的内部实现逻辑就是不断地从事件队列中取出事件,并调用相应的时间处理方法,直到没有事件需要处理为止.同时,runloop还可以等待新事件的到来,以提高线程的利用率.

10.多线程的理解

iOS多线程是指在iOS应用程序中同时运行多个独立的线程,以实现不同的任务,提高应用程序的性能和响应速度.在iOS中,多线程主要使用Grand Central Dispatch(GCD)和NSOperation两种方式.

GCD是一个轻量级的任务调度框架,提供了一种简单易用的方式来实现多线程编程.它可以自动管理线程的生命周期,并根据系统负载自动调整并发线程数,以达到最优的性能变现.通过使用GCD,开发人员可以将任务提交给队列,然后GCD会自动选择合适的线程来执行任务.

1. 'dispatch_async(dispatch_queue_t queue,dispath_block_t block)':异步执行任务,将任务添加到指定的调度队列中.
2. 'dispath_sync(dispatch_queue_t queue,dispatch_block_t block)':同步执行任务,将任务添加到指定调度队列中,并等待任务完成.
3. 'dispatch_group_async(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block)':异步执行任务,并将任务添加到指定的调度组中,可以使用'dispatch_group_notify()'方法在所有任务执行完成后才执行某些操作.
4. 'dispatch_barrier_async(dispatch_queue_t queue,dispatch_block_t block)':在指定的调度队列中执行一个栅栏函数,可以用来保证在栅栏任务之前提交的任务都完成在执行栅栏任务,以及在栅栏任务执行完成后才执行之后提交的任务
5. 'dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block)':延时指定时间后执行任务
6. 'dispath_apply(size_t iterations,dispath_queue_t queue,void(^block)(size_t))':同步执行多次任务,用于执行重复性的任务.
7. 'dispatch_once(dispatch_once_t *predicate,dispatch_block_t block)':只执行一次任务,用于单利模式的创建

NSOperationQueue 是一个基于GCD的高级任务调动框架,它提供了更高层次的抽象和控制.它可以让开发人员更方便的控制任务之间的关系和执行顺序,并且可以取消或暂停任务.NSOperationQueue还可以通过设置最大的并发数来限制通知执行的任务数,从而更好的控制系统资源.

1. '-addOperation:'添加操作到队列中
2. '-addOperation:waitUntilFinished:'添加一组操作到队列中,并指定是否等待完成所有的操作再返回.
3. '-cancelAllOperations':取消队列总所有未执行的操作.
4. 'setSuspended':暂停或继续队列中的所有操作.
5. 'waitUntilAllOperationsAreFinished':等待队列中所有的操作完成再返回.

在iOS中,多线程编程可以通过实现许多功能,比如在后台进行网络请求、处理大量数据、响应用户交互、执行耗时操作等等.但是多线程编程也有一些风险,比如静态条件、死锁、内存泄露等问题,因此在进行多线程编程时需要特别的小心.

11.自动释放池原理

自动释放池是Objct-C语言中用来管理内存的一种机制,它用于在内存中储存临时对象,并在池被释放时自动释放这些对象,从而避免内存泄露的问题.

自动释放池原理: 自动释放池在创建时会生成一个栈结构,用于储存对象的内存地址.当程序执行到@autoreleasepool时,就会向栈中添加一个新的池,然后在池中执行一系列操作.当池被销毁时,所有在池中生成的对象就会被自动释放,内存会被回收.同时,池中也会从栈中弹出,返回到上一个池或者主循环中.

什么时候使用自动释放池? 在Objct-C中,对象的内存管理是由程序员手动进行的,而自动释放池可以在一定程度上简化内存管理的过程,使代码更加简洁宜都.在以下情况下,通常需要使用自动释放池:

1.当创建一些临时对象时,可以将这些对象加入到自动释放池中,以便在池被释放时自动释放这些对象,从而避免内存泄露.

2.在循环中创建大量对象时,可以使用自动释放池来及时回收内存,避免内存张勇过高导致程序崩溃.

3.在多线程编程中,使用自动释放池可以确保不同程序的内存管理不会相互干扰,避免出现内存错误.

12.weak修饰的属性在category的内部是如何实现的?

在Objct-C中,属性的关键字'weak'用于修饰对象类型的属性,表示属性所引用的对象时弱引用,即当对象的所有者释放该对象时,该属性会自动变成'nil'

当一个类被声明为'@interface MyClass: NSObjct'时,该类会默认继承自"NSObjct"类,并具有一个名为'isa'的指针,指向其类对象(class Objct).类对象本质上也是一个普通的对象,它储存了类的元数据、属性列表、方法列表等信息.当我们定义一个属性时,实际上是想该类对象的属性列表中添加了一个属性描述符(property descriptor),该属性描述符包含属性名、属性类型、访问空时修饰符等信息.

当'weak'关键字修饰一个属性时,编译器会想类对象的属性列表中添加一个关于该属性的额外信息,包括属性的弱引用和与之相关的变量.具体来说,编译器会为该属性创建一个名为'_propertyName'的实例变量,并向类对象的属性列表中添加一个关于该属性的描述符,该描述符会包含属性名、属性类型、访问控制修饰符、关联的实例变量名以及弱引用标识符等信息.在该类的实例化过程中,每个实例都会拥有一个指向该类对象的指针'self',当我们为该属性赋值时,实际上是向该实例的'_propertyName'实例变量中存储一个弱引用.

需要注意的是,'weak'关键字只能用于修饰对象类型的属性,而不能用于修饰基本数据类型或结构体类型属性.此外,'weak'关键字只在Objct-c中有效,在Swift中应使用'weak var'关键字.