ios 知识

IOS 类文件.h和.m中@interface的区别
大家都知道我们在创建类文件时会发现:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController


@end

#import "ViewController.h"

@interface ViewController ()

@end

那么他们之间有何区别呢?

1、.h里的@interface,是典型的头文件,它的属性(@property)和方法(functions)都是能够向其他类公开的。我们都知道有三种权限@private,@protected,@public。写在.h里的默认是@protected权限。

2、.m里的@interface,我们也可以称之为扩展(class extension),是.h文件中@interface的补充。可以增加属性,方法和成员变量,但是只能在.m文件里可见,对外是不开放的。在.m里的默认是@private权限

@END.

@property的作用和atomic和nonatomic

参考
当我们写下@property NSObject *foo时,编译器帮我们做了以下几件事:

创建实例变量_foo
声明foo属性的setter、getter方法
实现foo属性的setter、getter方法
@property 的本质是什么?

@property = ivar + getter + setter;

实例变量+get方法+set方法,也就是说使用@property 系统会自动生成setter和getter方法;

@property的属性关键字详解
atomic和nonatomic
atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作。

atomic:系统生成的 getter/setter 会保证 get、set 操作的完整性,不受其他线程影响。getter 还是能得到一个完好无损的对象(可以保证数据的完整性),但这个对象在多线程的情况下是不能确定的。

举个🌰
如果线程 A 调了 getter,与此同时线程 B 、线程 C 都调了 setter——那最后线程 A get 到的值,有3种 可能:可能是 B、C set 之前原始的值,也可能是 B set 的值,也可能是 C set 的值。同时,最终这个属性 的值,可能是 B set 的值,也有可能是 C set 的值。所以atomic可并不能保证对象的线程安全。

也就是说:如果有多个线程同时调用setter的话,不会出现某一个线程执行完setter全部语句之前,另一个线程开始执行setter情况,相当于函数头尾加了锁一样,每次只能有一个线程调用对象的setter方法,所以可以保证数据的完整性。

atomic所说的线程安全只是保证了getter和setter存取方法的线程安全,并不能保证整个对象是线程安全的。

nonatomic:就没有这个保证了,nonatomic返回你的对象可能就不是完整的value。因此,在多线程的环境下原子操作是非常必要的,否则有可能会引起错误的结果。但仅仅使用atomic并不会使得对象线程安全,我们还要为对象线程添加lock来确保线程的安全。

nonatomic的速度要比atomic的快。atomic是Objc使用的一种线程保护技术,这种机制是耗费系统资源的,所以在iPhone这种小型设备上,我们基本上都是使用nonatomic,而对象的线程安全问题则由程序员代码控制。

atomic与nonatomic的本质区别其实也就是在setter方法上的操作不同

nonatomic对象、atomic对象setter和getter方法的实现:

/// nonatomic对象
- (void)setCurrentImage:(UIImage *)currentImage
{
    if (_currentImage != currentImage) {
        [_currentImage release];
        _currentImage = [currentImage retain];
    }
}
- (UIImage *)currentImage
{
    return _currentImage;
}


/// atomic对象
- (void)setCurrentImage:(UIImage *)currentImage
{
    @synchronized(self) {
        if (_currentImage != currentImage) {
            [_currentImage release];
            _currentImage = [currentImage retain];
        }
    }
}

- (UIImage *)currentImage
{
    @synchronized(self) {
        return _currentImage;
    }
}


总结:可以发现几乎所有代码的属性设置都会使用nonatomic,这样能够提高访问性能,在iOS中使用锁机制的开销较大,会损耗性能。

assign

1.这个修饰词是直接赋值的意思 , 整型/浮点型等数据类型都用这个词修饰 .
2.如果没有使用 weak strong retain copy 修饰 , 那么默认就是使用 assign 了.
3.当然其实对象也可以用 assign 修饰 , 只是对象的计数器不会+1 . ( 与 strong 的区别 )
4.如果用来修饰对象属性 , 那么当对象被销毁后指针是不会指向 nil 的 . 所以会出现野指针错误 . ( 与weak的区别 )

weak

weak是弱引用,用weak描述修饰或者所引用对象的计数器不会加一,并且会在引用的对象被释放的时候自动被设置为nil,大大避免了野指针访问坏内存引起崩溃的情况,另外weak还可以用于解决循环引用。

weak原理概括
weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址数组。weak的底层实现的原理是什么?

Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash表,Key是所指对象的地址,value是weak指针的地址(这个地址的值是所指对象指针的地址)数组。

为什么value是数组?因为一个对象可能被多个弱引用指针指向

strong

在ARC环境下,只要某一对象被一个strong指针指向,该对象就不会被销毁。如果对象没有被任何strong指针指向,那么就会被销毁。在默认情况下,所有的实例变量和局部变量都是strong类型的。可以说strong类型的指针在行为上跟非ARC下的retain是比较相似的

copy

浅拷贝

只是将对象内存地址多了一个引用,也就是说,拷贝结束之后,两个对象的值不仅相同,而且对象所指的内存地址都是一样的。
深拷贝

拷贝一个对象的具体内容,拷贝结束之后,两个对象的值虽然是相同的,但是指向的内存地址是不同的。两个对象之间也互不影响,互不干扰。

@interface 和 @implementation

在这里插入图片描述
OC中的类必须包括两部分,interface部分和implementation部分,这才是oc中的一个类的完整声明;
OC中将成员变量和成员方法的声明部分放置在interface部分中,包括继承关系,protocal实现关系,都在interface里面的头部进行声明,
然后将实现部分放置在implementation部分中,相当于是将类拆分成声明和实现两部分,这两部分缺一不可,所以在OC中,不妨不要将interface叫做接口,直接叫做类声明部分来得容易理解多了,简而言之,oc中interface是类的一个部分,和implementation共同组成一个完整的类。

- (instancetype)init

这个就是该类的初始化
参考

 (instancetype)init{
    self=[super init];
    if (self) {
        
    }
    NSLog(@"=========in MyView.init 函数里");
    return  self;
}

+和-

+号是static
在这里插入图片描述

self 和 super

self指的是类对象本身;
super是父类对象本身;
self用来调用本类对象的方法;
self关键字先从本类中查找是否有此方法,如果没有,再从父类中调用此方法;
super调用从父类继承下来的方法;
super关键字直接调用父类中定义的方法.

//方法定义

(void)shopping;//不带参数的方法
-(void)goshopping:(float)price; //带参数的方法
-(id)someMethod:(int)someArg someOtherArgName:(int)someOtherArg; //otherParameter是参数的别名(第一个参数的别名省略),在函数调用时方便指定。

new、alloc

Object-C中的方法调用形式采用消息发送的方式,通常调用的形式如:

[someObject someMethod:firstArg someOtherArgName:otherArg];

实例的初始化也采用消息发送的形式,可以简单的调用类型的new方法来获取一个实例对象,简单实例化的方法通常是:

someObject *obj = [someObject new]; //类的实例化

new 方法的实际过程是调用 alloc 和 init 方法,因此如果需要采用自定义的方法来初始化实例,则需要自己重写 init 方法,通常的初始化方式为:

someObject *obj = [[someObject alloc] init]; //采用无参数的init实例化   
someObject *obj = [[someObject alloc] initWithArg:Arg]; //采用参数的实例化 

调用方法

在这里插入图片描述
方法调用的本质就是放对象发送消息

/* new 会调用 init方法 */
People *man = [People new];//类调用new
People *man = [[People alloc] init];//类创建了个对象,调用init

//属性方法调用eat的方式
[man eat];
//类方法调用eat方式
[People eat];
[[People class] eat];

//还有一种不常用的调用方式
[对象/类 performSelector:@selector(eat)];

//底层实现
objc_msgSend(对象/属性, @selector(eat));

//_render对象调用registerModule方法,参数是_lookupDescriptor

[_render registerModule:_lookupDescriptor];

#import <Foundation/Foundation.h> 导入基本框架

#import <Foundation/Foundation.h>指用来引入框架Foundation下的/文件名Foundation.h,如果直接写成#import 引1用不到对应的头文件,Foundation.h文件是存在Foundation框架下的,调用的时候需按层次结构找到相应的文件来引用;
在c语言中,可以直接引用<stdio.h>引1用相应的文件,但在Objective-C编程中,GCC编泽器选择是
在Foundation框架下编译的

打开Xcode的preference
command + <

“namespace”

在C++中,"namespace"是用来创建命名空间的关键字。命名空间提供了一种将一组相关的变量、函数、类等组织在一起的机制,以避免命名冲突和提高代码的可读性。

要创建一个名为"agora"的命名空间,可以使用以下语法:

namespace agora {
    // 命名空间内的代码
    // 可以定义变量、函数、类等
}

在上述代码中,我们使用"namespace"关键字创建了一个名为"agora"的命名空间,并在花括号内编写该命名空间的代码。

例如,可以在"agora"命名空间中定义一些变量和函数:

namespace agora {
    int age = 25; // 声明一个age变量,值为25

    void printName() {
        std::cout << "My name is Agora" << std::endl;
    }
}

在其他地方的代码中,可以使用"agora"命名空间中定义的变量和函数:

int main() {
    std::cout << agora::age << std::endl;    // 输出: 25
    agora::printName();    // 输出: My name is Agora

    return 0;
}

通过使用命名空间,可以将相关的代码组织在一起,并通过命名空间限定符(如"agora::")访问其中的成员。这样可以避免命名冲突,并提高代码的可维护性和可读性。

“std::cout”

"std::cout"是C++标准库中的一个输出流对象。它是用于向标准输出设备(通常是控制台)输出信息的。"std"是C++标准库命名空间的缩写,"cout"是C++标准库中定义的用于输出的流对象。

使用"std::cout"可以将数据和文本输出到控制台。它通常与插入运算符(<<)一起使用,将要输出的内容插入到输出流中。例如:

#include <iostream>

int main() {
    int number = 10;
    std::cout << "The value of number is: " << number << std::endl;
    return 0;
}

在上述代码中,通过使用"std::cout"和插入运算符(<<),我们将字符串 "The value of number is: " 和变量 “number” 的值输出到控制台。输出结果将是 “The value of number is: 10”。

"std::endl"是一个用于输出换行符的特殊符号。在上述代码中,它用于在输出结束后插入一个换行符,使下一个输出在新的一行开始。

通过使用"std::cout",我们可以在控制台上显示程序运行过程中的信息,从而方便调试和查看程序的输出结果。

std::cout中的::

在C++中,"::"是作用域解析运算符。它用于指定命名空间、类、结构体、枚举等的成员或静态成员。

在"std::cout"中,“std"是命名空间,“cout"是该命名空间中的一个成员,即输出流对象。通过使用”::”,我们可以指定"cout"是属于"std"命名空间中的。

示例:

#include <iostream>

int main() {
    int number = 10;
    std::cout << "The value of number is: " << number << std::endl;
    return 0;
}

在上述代码中,"std::cout"中的"std"指定了输出流对象"cout"是属于"std"命名空间中的。这样,我们可以通过"std::cout"来访问和使用输出流对象。

class VideoFrameObserver : public media::IVideoFrameObserver

这段代码是C++中的继承语法。它定义了一个名为VideoFrameObserver的类,该类继承自media命名空间中的IVideoFrameObserver类。

继承(inheritance)是面向对象编程中的一种重要概念,它允许一个类(称为子类或派生类)从另一个类(称为父类或基类)继承属性和行为。通过继承,子类可以直接访问父类中的公共成员,避免了重复编写相同的代码。

在这段代码中,VideoFrameObserver类通过关键字"public"继承了IVideoFrameObserver类。这意味着VideoFrameObserver类将继承IVideoFrameObserver类中的所有公共成员。通过继承,VideoFrameObserver类可以使用IVideoFrameObserver类中定义的方法和变量。

示例:

namespace media {
    class IVideoFrameObserver {
        // 父类的方法和变量
    };
}

class VideoFrameObserver : public media::IVideoFrameObserver {
    // 子类的方法和变量
};

在上述示例中,IVideoFrameObserver是一个位于media命名空间中的类。VideoFrameObserver类通过继承关系,获得了IVideoFrameObserver类中的方法和变量,可以在VideoFrameObserver类中使用它们。

long long

“long long” 是一种整数数据类型,表示长整型。在C++中,long long是一种有符号整数类型,可以表示更大范围的整数值。

具体的大小取决于不同的编译器和操作系统,但通常情况下,long long类型至少占用8个字节(64位),可以表示的整数范围通常为-9,223,372,036,854,775,808到9,223,372,036,854,775,807。

示例代码:

long long num = 1234567890123456789LL; // 后缀LL表示字面量是long long类型
std::cout << num << std::endl;

在上述示例中,我们声明了一个long long类型的变量num,并将一个长整数值赋给它。然后使用std::cout输出变量的值。

*observer

在给定的函数签名中,*observer 是一个指针类型的参数。

* 是一个指针运算符,用于声明指针类型或者用于解引用指针。在这个函数签名中,*observer 表示 observer 是一个指针变量,它指向某个特定的内存地址。

指针是一个存储内存地址的变量,它可以指向其他变量或对象。通过使用指针,我们可以间接地访问和修改指向的数据。

在上述函数签名中,*observer 的类型不能直接确定,因为在函数签名中只给出了指针的类型为 void*void* 是一种通用的指针类型,可以指向任意类型的数据。这种类型的指针可以存储指向任何类型的内存地址,但在使用时需要进行类型转换。

因此,在函数实现中,我们需要根据具体的使用情况,将 void* 类型的指针转换为目标类型的指针,然后再进行相应的操作。

void *render是什么意思

void *render 是一个指向 render 的指针参数,它的类型是 void*

void* 是一种通用的指针类型,可以指向任意类型的数据。这种类型的指针可以存储指向任何类型的内存地址,但在使用时需要进行类型转换。

在上述函数签名中,void *render 表示 render 是一个指针变量,它指向某个特定的内存地址。具体的类型信息没有给出,所以在函数实现中需要根据具体的使用情况,将 void* 类型的指针转换为目标类型的指针,然后再进行相应的操作。

通常情况下,将指针声明为 void* 类型可以提供更大的灵活性,因为它可以接受任何类型的指针作为参数,而不需要进行类型匹配。但在实际使用时,需要根据具体的需求进行类型转换,以便正确操作指向的数据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值