Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to get started with Objective-C

2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

How to start Objective-C, many novices are not very clear about this, in order to help you solve this problem, the following editor will explain for you in detail, people with this need can come to learn, I hope you can gain something.

Preface

Objective-C (hereinafter referred to as OC), which is extended by C language and Smalltalk, is a superset of C language. The biggest difference is that OC is object-oriented, and its Martian writing is quite painful for students who have been engaged in Java development before. One of the biggest features of OC is the use of "message structure" rather than "function calls", so the code executed at run time is determined by the runtime environment, while Java is determined by the compiler. I feel that the quality of articles about IOS learning is lower than that of Android, which may be the reason why the Apple system is closed. This article focuses on introducing the commonly used syntax. By comparing Java and combining my introductory process and experience, we can help students with needs to quickly master the basic programming of OC and lay the language foundation for the introduction of IOS. First of all, the following is to write the first line of code, congratulations on entering the OC learning phase.

Int main (int argc, char * argv []) {@ autoreleasepool / / create automatic memory release pool {/ / printout NSLog (@ "hello world ios!"); return 0;}}

The file extension of the OC code is described below:

File extension type .h header file, which is used to declare classes, attributes, variables, functions, etc. M implementation file, life implementation or extension of header file. Mm implementation file, usually C++ code

If the implementation file needs to introduce a header file, it is recommended to use # import, which has the same effect as # include. It is optimized to ensure that the same file will only be introduced once, so it tends to use # import.

Basic data type

Including: int float double char

Type byte formatted output char1%cint4%i,%x,%ounsigned int4%i,%x,%oshort int2%hi,%hx,%hounsigned short int2%hi,%hx,%holong int8%li,%lx,%lounsigned long int8%lu,%lx,%lolong long int8%lli,%llx,%llounsigned long long int8%llu,%llx,%llofloat4%fdouble8%flong double16%Lf other data types id type

Objects of any data type can be stored, like the Object class in Java, which is defined as a pointer to the object (which is itself a pointer), so defining a type such as id instance = nil;id is the basis of polymorphism and dynamic binding.

BOOL Typ

The Boolean value is YES/NO or 1amp 0. Java corresponds to true/false.

Nil and Nil

Nil is the equivalent of null in Java, representing an object whose pointer points to null. Nil defines a class that points to an empty object rather than an object.

NSString (immutable string)

Strings are very important and commonly used, and be sure to master the basic usage, including creation, interception, traversal, comparison, case conversion, search, and so on. The semantics are basically similar to Java.

/ / string NSString * str1 = @ "ABC3456789"; / / spliced into a new string NSString * str2 = [str1 stringByAppendingString:@ "wwww"]; NSLog (@ "str =% @", str2); / / traversing for (int I = 0; I)

< [str2 length]; i++) { char temp = [str2 characterAtIndex:i]; NSLog(@"字符串第 %d 位输出 %c", i, temp);}//比较// sEqualToString方法 :比较字符串是否完全相等,大小写不一样也无法完全匹配。//hasPrefixe方法:逐一匹配字符串头部。haSuffix方法:匹配字符串的尾部if ([str2 isEqualToString:str1]) { NSLog(@"相等");}if ([str2 hasPrefix:@"www"]) { NSLog(@"有该头部");}if ([str2 hasSuffix:@"www"]) { NSLog(@"有该尾部");}if ([str2 compare:str options:NSCaseInsensitiveSearch | NSNumericSearch] == NSOrderedSame) {}NSLog(@"比较结果:%d", [str2 caseInsensitiveCompare:str1]);//大小写转换NSLog(@"str3转大写:%@",[str2 uppercaseString]);NSLog(@"str3转小写:%@",[str2 lowercaseString]);NSLog(@"str3首字母转大写:%@",[str2 capitalizedString]);//字符串截取NSRange rang = NSMakeRange(2, 2);NSLog(@"str3截取:%@",[str2 substringWithRange:rang]);//搜索NSRange rang1 = [str2 rangeOfString:@"www"];NSLog(@"location: %d,length: %d",rang1.location,rang1.length);//替换//全部替换NSString *str3 = [str2 stringByReplacingOccurrencesOfString:@" " withString:@"@"];NSLog(@"替换后字符串为%@", str3);//局部替换NSString *str4 = [str2 stringByReplacingCharactersInRange:rang withString:@"met"];NSLog(@"替换后字符串为%@", str4);NSMutableString(可变字符串) 创建对象的基本写法是[[NSMutableString alloc]init],*号代表对象,[]代表方法调用,只能通过类或者对象才能调用。[NSMutableString alloc]类似Java中new得到一个对象,然后再调用init初始化方法。 //创建对象并初始化 NSMutableString *mStr = [[NSMutableString alloc]init]; //appendstring:向字符串尾部添加一个字符串。 //appendFormat:向字符串尾部添加多个类型的字符串,可以添加任意数量与类型的字符串。 [mStr appendString:@"hello world!"]; NSLog(@"字符串创建%@", mStr); [mStr deleteCharactersInRange:[mStr rangeOfString:@"hello"]]; //删除 NSLog(@"字符串删除%@", mStr); //插入 [mStr insertString:@"love you" atIndex: mStr.length]; NSLog(@"字符串插入%@", mStr);NSInteger、NSUInteger和NSNumber NSInteger不是一个对象,而是基本数据类型中的typedef,NSUInteger是无符号的。 当需要使用int类型的变量时,推荐使用NSInteger,这样不需要考虑设备是32位或者64位。NSNumber是一个类,用于包装基本数据类型成为对象,可以理解为Java中的装箱,为一些集合只能存放对象使用,通过字面量方式非常方便将基本数据类型转成对应的对象。例如: //包装NSNumber *intNumber = [[NSNumber alloc]initWithInt:43];//或者字面量方式NSNumber *intNumber1 = @43;//还原基本数据类型,解包NSLog(@"%d",[intNumber intValue]);集合 集合不能接受nil,nil是作为集合结束标识符。 1. NSArray(不可变) 类似Java中的ArrayList,可以存储不同类型的对象,一般情况下数组元素的类型是相同的,特点是有序、可重复。下面展示一位数组的基本操作: //字面量创建方式 NSArray *arr2 = @[@"aaa",@"bbbbb"];//工厂方法创建 NSArray *array = [[NSArray alloc] initWithObjects:@"1", @"2", nil]; //取最后一个元素 [array lastObject];// 取第一个元素 [array firstObject];// 数组是否包含某个元素 [array containsObject:@"1"];// 数组的大小 int count = (int) array.count;// 第一种方式遍历 for (int i = 0; i < count; i++) { NSString *_str = [array objectAtIndex:i]; } 那么数据要求是多维的呢?多维数组可以理解为数组的数组,通过嵌套的方式,创建如下: // 字面量创建二维数组并访问NSArray *arr2 = @[@[@11, @12, @13], @[@21, @22, @23], @[@31, @32, @33]];// 字面量访问方式(推荐)NSLog(@"arr2[2][2]:%@", arr2[2][2]);// 数组对象函数访问NSLog(@"arr2[2][2]:%@", [[arr2 objectAtIndex:2] objectAtIndex:2]);2. NSMutableArray(可变的) 派生于NSArray,理解为动态数组,提供增加、删除、插入、替换等语法糖。 //创建,当然还有其他方式 NSMutableArray *mutableArr = [NSMutableArray arrayWithObjects:@"one",@"two",@"three", nil]; //添加 [mutableArr addObject:@"hello"]; //替换 [mutableArr replaceObjectAtIndex:2 withObject:@"tihuan"]; //删除 [mutableArr removeObjectAtIndex:1]; //插入 [mutableArr insertObject:@"ios" atIndex:1]; 多维数组创建方式如下: // 初始化作为列的数组,看做4列NSMutableArray *columnArray = [[NSMutableArray alloc]initWithCapacity:4];// 初始化2个一维数组,每个一维数组有4个元素,看做1行4列,2行加起来就是2行4列NSMutableArray *rowArray1 = [[NSMutableArray alloc]initWithCapacity:4];NSMutableArray *rowArray2 = [[NSMutableArray alloc]initWithCapacity:4];// 每个行依次增加数组元素// 第一行[rowArray1 addObject:@"11"];[rowArray1 addObject:@"12"];[rowArray1 addObject:@"13"];[rowArray1 addObject:@"14"];// 第二行[rowArray2 addObject:@"21"];[rowArray2 addObject:@"22"];[rowArray2 addObject:@"23"];[rowArray2 addObject:@"24"];// 分别打印数组NSLog(@"myRowArray1: %@", rowArray1);NSLog(@"myRowArray2: %@", rowArray2);NSLog(@"myColumnArray: %@", columnArray);字典 类似于Java中的HashMap,是一种映射型数据结果,存储键值对,有可变和不可变两种类型。 NSDictionary 主要特点是不可变,如果集合初始化完成,将内容无法修改,无序。 //标准创建 NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@"cat",@"name1",@"dog",@"name2", nil]; //字面量创建 NSDictionary *dict1 = @{@"name1":@"cat",@"name2":@"dog"}; //第一种遍历 for (NSString *key in [dict1 allKeys]) { NSLog(@"key: %@,value: %@", key, dict1[key]); } //第二种遍历方式,通过遍历器 NSEnumerator *rator = [dict keyEnumerator]; NSString *temp; while (temp = [rator nextObject]) { NSLog(@"%@", temp); } //获取元素 dict1[@"name"]; [dict1 objectForKey:@"name"]; //集合元素的个数 NSInteger count = dict1.count; //沙盒文件存储和读取Plist [dict5 writeToFile:@"路径" atomically:YES]; NSDictionary *dict7 = [NSDictionary dictionaryWithContentsOfFile:@"路径"];NSMutableDictionary NSMutableDictionary是NSDictionary的子类。NSMutableDictionary是可变的,动态添加、更改、删除元素,因此不能使用字面量方式(@{})来创建一个可变字典。如果是不可变字典,出现了同名的key,那么后面的key对应的值不会被保存,反之是可变字典,出现了同名的key,那么后面的值会覆盖前面的值。 //创建NSMutableDictionary *dict = [NSMutableDictionary dictionary];//添加[dict setObject:@"dog" forKey:@"name"];[dict setValue:@"18" forKey:@"age"];//会将传入字典中所有的键值对取出来添加到dict中[dict setValuesForKeysWithDictionary:@{@"name1":@"dog"}];//取元素[dict objectForKey:@"name"];dict[@"name"];//删除[dict removeAllObjects];[dict removeObjectForKey:@"name"];[dict removeObjectsForKeys:@[@"name", @"age"]];//更新,如果利用setObject方法给已经存在的key赋值,新值会覆盖旧值[dict setObject:@"20" forKey:@"age"];dict[@"age"] = @"30";NSSet && NSMutableSet 具有很好的存取和查找功能,与NSArray相比NSSet的元素没有索引,特点是无序,不可重复,类似Java中的HashSet,其中NSMutableSet提供计算交并集的方法。 NSSet存储元素的过程:

Note: it is recommended to create objects literally, which can shorten the length of the code and increase readability. But when creating an array, it should be noted that if it contains nil, it will throw an exception, because the literal amount is actually "syntax sugar", which is equivalent to creating an array first, and then adding all the objects to it, ensuring that the array does not add nil.

Message passing

The preface mentions that one of the most important features of Objective-C is that it inherits the Smalltalk message delivery model, so the method call preparation in OC is message delivery, the relationship between category and message is loose, the calling method is to send a message to the object, and the method is a response to the message, the processing of all messages is not dynamically determined until run time (that is, runtime), and it is left to the class to decide how to handle the received message. Summary is that a class does not guarantee that it will respond to a received message and throws an exception when it receives a message that cannot be processed.

Call the Java or C++ method:

Obj.method (argument)

The OC method call:

[obj method: argument]

We all know that in Java or C++, if the class does not define a method method, the compilation will definitely not pass, but in OC, the understanding is to send a message of method to obj,obj and then decide how to respond to the message, and run if the method method is defined in the class, otherwise, there is no run-time exception.

Class

All object-oriented programming has the concept of classes, which are used to encapsulate data, and such language features include encapsulation, inheritance, and polymorphism. An OC object is an instance of a class at run time, containing instance variables declared by the class, memory copies, pointers to class members, and so on. Because OC is a superset of the C language, the class consists of two parts, interface and implementation. Create a new People class, @ interface is the beginning of the interface declaration, @ end ends, and all OC compilation instructions start with "@". The implementation of the class begins with the @ implementation instruction and ends with @ end. Corresponding to People.h and People.m files, the following figure shows the class declaration (People.h), which mainly includes inheritance relationships, member variables, properties, method declarations, and so on. The specific implementation of the method is in People.m.

The following figure shows the method declaration:

Of course, variables can be defined not only in Interface blocks, but also in Implementation blocks. The difference between the two is that the access permissions are different.

The default permission of the former is protected, while the entity variable of implementation block defaults to private, so category private can be placed in implementation block.

Access modifier

@ public: can be accessed from any location.

Protected: the modifier of the member variable by default.

Private: a variable is limited to the class that declares it and is not allowed to be inherited.

Package: limited to the current package, similar to the concept of the Java package.

Attribute

Member variables are used within the class, and properties are used as interfaces to access member variables outside the class to encapsulate the data of the object. through the @ property declaration, the compiler automatically generates setter and getter methods, a process called "automatic synthesis". The @ synthesize syntax in the class implementation file can specify the name of the instance variable, which is generally not recommended. The @ dynamic syntax tells the compiler not to synthesize automatically, and access modifiers are rarely used in OC, mainly by declaring the value of the property.

There are five common trait modifiers for attributes:

Assign: assigns values to basic data types.

Strong: defines a "ownership relationship" in which when a property sets a new value, it retains the new value, releases the old value, and then sets the new value.

Weak: in contrast to strong, the property value is also emptied when the object referred to by the property is destroyed.

Copy: the setting method does not keep the new value, but makes a copy.

Nonatomic: non-atomic, non-thread-safe type.

QroomA: why do the properties of NSString, NSArray, and NSDictionary use copy? what is the deep and shallow copy of the collection?

The function of the copy attribute is to automatically copy a copy of memory when assigning a value to a variable, and modifying the new variable will not affect the old variable. In the Apple specification, NSString,NSArray,NSDictonary recommends the use of the copy attribute, while its NSMubtableString,NSMutableArray and NSMutableDictonary properties use the strong attribute.

NSString * sourceString = [NSString stringWithFormat:@ "hello ios"]; / / do not generate new memory space NSString * copyStr = [sourceString copy]; / / generate new memory space NSMutableString * mutableStr = [sourceString mutableCopy]; NSLog (@ "sourceString:% @% p", sourceString,sourceString); NSLog (@ "copyStr:% @% p", copyStr,copyStr); NSLog (@ "mutableStr:% @% p", mutableStr,mutableStr)

Using the strong attribute, it is possible to point to a mutable object, which will be affected if the mutable object is modified externally. For example:

/ / Code block NSMutableString * string = [NSMutableString stringWithString:@ "origin"]; / / copyNSString * stringCopy = [string copy]; NSLog (@ "string address is:% p", string); NSLog (@ "stringCopy address is:% p", stringCopy)

Result: memory address is different

NSMutableString * string = [NSMutableString stringWithString:@ "origin"]; / / NSString * stringCopy = [string copy]; NSString * stringCopy = string; [string appendString:@ "change"]; NSLog (@ "string address is:% p", string); NSLog (@ "stringCopy address is:% p", stringCopy)

Result: same memory address

Conclusion:

A mutable object pointing to an immutable object causes the value of the immutable object to be tampered with, so the copy attribute is required. Use @ property to declare that NSString, NSArray, and NSDictionary often use the copy keyword because they have corresponding variable types NSMutableString, NSMutableArray, and NSMutableDictionary, which may assign values to each other. In order that the content in the immutable object will not be inadvertently changed, you should make a copy when setting the new attribute value.

Shallow copy:

In Java, if it is basic data, the value of the basic data is copied; if it is an object, the memory address is copied, and modifying the object will affect another object. In OC, the pointer is copied, and the copied pointer and the pointer of the original object point to the same memory address, so they also affect each other.

Deep copy:

In OC, not only the pointer is copied, but also what the pointer points to, and the pointer points to a different memory address, so the modification will not affect the original object.

In non-collection class objects: copy operation on immutable object is pointer copy (shallow copy), content copy during mutableCopy operation; copy and mutableCopy on mutable object are content copy (deep copy).

Method

The class method and the instance method are declared by "+" and "-" respectively. If the method has multiple parameters, the parameters are defined by colons after the method name, and the multiple parameters are separated by spaces. If the number of parameters is variable, use commas and ellipses. For example:

/ / No parameters-(void) print;// has parameters-(void) print: (int) an andB: (int) b; Construction method

The first is to override the init method, and the second is to customize.

/ * * rewrite initialization method * * /-(instancetype) init {self = [super init]; if (self) {_ peopleName = @ "hello ios";} return self;} / * * Custom initialization method * * /-(instancetype) initWithNameAndAge: (NSString *) name andAge: (int) age {self = [super init]; if (self) {_ peopleName = name; _ peopleAge = age } return self;} create a class object

All references to objects and classes are realized through pointers, strictly speaking, pointers are an address and a constant, and pointer variables can be assigned different pointer values, the created object is a pointer variable, and a People object is created through [People alloc], which allocates memory, and init is the initialization object. There are two ways to construct methods, the first is to override the init method, and the second is to customize it.

People * p1 = [[People alloc] init]; / / call the custom constructor People * p3 = [[People alloc] initWithNameAndAge:@ "mingzi" andAge:12]; / / call the method [p3 print]

In OC 2.0, if you create an object that does not require parameters, you can use new directly:

People * p1 = [People new]

Self

As a keyword of OC, it represents the object of the current class, similar to this in Java. The biggest function is to let one method in the class call another method or member variable of the class. It can be understood that "self represents who the current class calls this method."

Inherit

Like Java, you can only inherit only, allowing at most one direct parent class. For example, define a parent class Computer and a subclass MacBook. Note that method rewriting is similar to Java, and the subclass overrides the parent method without redeclaring the override method, but directly overrides the target method in the implementation section. If you need a subclass to call the method of the parent class, you can call it through the super keyword.

/ / Computer.h file # import @ interface Computer: NSObject@property (nonatomic,strong) NSString * name;- (void) calculate;@end// Computer.m#import "Computer.h" @ implementation Computer@synthesize name;- (void) calculate {NSLog (@ "i can calculate") @ end// MacBook.h#import "Computer.h" @ interface MacBook: Computer@end// MacBook.m#import "MacBook.h" @ implementation MacBook@end//main.mint main (int argc, char * argv []) {@ autoreleasepool {MacBook * macBook = [[MacBook alloc] init]; macBook.name = @ "mac"; [macBook calculate];}} Polymorphism

Encapsulation, inheritance and polymorphism are the three major features of object-oriented programming languages. OC polymorphism is the different ways in which different objects respond to the same message. The actual process is mainly divided into three types:

Inherit

Rewrite

A pointer to a subclass points to a parent class

You can see that it is similar to the polymorphism of Java, which should be easier to understand. Note that there is no method overloading, which is not allowed in OC.

Runtime

Example: add a class Person with Runtime. Person has name attribute and sayHi method.

# import # import "AppDelegate.h" # import # import void sayHi (id self, IMP _ cmd, id some) {/ / self refers to the class NSLog passed by calling the method (@ "% @ says:% @, I% @ name"], some, object_getIvar (self, class_getInstanceVariable ([self class], "_ age") } int main (int argc, char * argv []) {@ autoreleasepool {/ / this method dynamically creates a class, arg1: which class is inherited from arg2: the name of the new class arg3:extraBytes Class Person = objc_allocateClassPair ([NSObject class], "Person", 0) / / add two instance variables name and age,arg2: variable name, arg3: memory address size, arg5: variable type class_addIvar (Person, "_ name", sizeof (NSString *), log2 (sizeof (NSString *)), @ encode (NSString *)); class_addIvar (Person, "_ age", sizeof (int), sizeof (int), @ encode (int)) / / Registration method name SEL s = sel_registerName ("say:"); / / arg3:IMP is an abbreviation for "implementation", and this function pointer determines which code is finally executed / / arg4: the parameters of the method and the return value class_addMethod (Person, s, (IMP) sayHi, "vested implementation @") / / create an entity object id peopleInstance = [[Person alloc] init]; / / assign a value to the object's name instance variable. Here is the second assignment method [peopleInstance setValue:@ "XQM" forKey:@ "name"]; / / Ivar nameIvar = class_getInstanceVariable (Person, "_ name"); / / object_setIvar (peopleInstance, nameIvar, @ "XQM") / / get instance variable Ivar ageIvar = class_getInstanceVariable (Person, "_ age"); / / assign object_setIvar (peopleInstance, ageIvar, @ 21) to the variable; / / call sayHi method, arg2: register the specified method Arg3: take the parameter ((void (*) (id, SEL, id)) objc_msgSend with a string (peopleInstance, s, @ "Hello"); / / when the call is completed, set the object to empty peopleInstance = nil; / / destroy the class through objc, and destroy a class instead of the object objc_disposeClassPair (Person);}}

The main process is:

Define class method-> objc_allocateClassPair create class-> class_addIvar add member variable to class-> sel_registerName register method name-> class_addMethod add definition method to class-> register class-> create class object-> class_getInstanceVariable get member variable, and use object_setIvar assignment-> objc_msgSend call method-> release object, destroy class

Category (category)

Objective-C borrows and extends the concept of "classification" in the Smalltalk implementation to help break up the code. The main features of categories are that they cannot add attributes or member variables, add class functions and separate class implementations. For example, the function of asynchronous loading of pictures has been added in UIImageView.

Interface UIImageView (ImageViewLoader)-(void) setOnlineImage: (NSString *) url placeholderImage: (UIImage *) image withRow: (NSNumber *) row;@end@implementation UIImageView (ImageViewLoader)-(void) setOnlineImage: (NSString *) url placeholderImage: (UIImage *) image withRow: (NSNumber *) row; {self.image = image; AsyncImageDownLoader * downloader = [AsyncImageDownLoader sharedImageDownloader]; [downloader startWithUrl:url delegate:self withRow:row];} @ endExtension (extension)

Extensions are also often used, the main features are the addition of ivar, for interface separation and so on. For example, the implementation file of ViewController adds @ interface ViewController (), supports defining attributes, and so on.

@ interface ViewController () @ property (nonatomic, copy) block bter implementation ViewController@end exception handling

OC's exception handling is very similar to that in Java, including four indicators, @ try, @ catch, @ throw, and @ finally. Code with possible exceptions is written in the @ try block. Code with exception handling logic written in the @ catch,@finally block is always executed, and the @ throw function is to throw an exception.

Agreement

Similar to the interface (interface) in Java, it supports protocol inheritance by defining a series of methods that are then implemented by compliant classes. Protocol methods can be marked as optional with the @ optional keyword and required with the @ required keyword, and the compiler will issue a check warning. Generally speaking, it can still be compiled and passed. Take a look at the syntax:

@ protocol ClickDelegate- (void) click;- (void) unclick;@end

The protocol is most often used in the delegation, which is divided into the principal and the agent. The principal is responsible for defining the protocol, holding the protocol through the id type and calling the protocol method, while the agent complies with the protocol, sets the protocol proxy object and implements the protocol method.

Block

Similar to the Lambda expression in Java, it is more complex, and the author's understanding has not reached a certain degree of explanation, so I will not explain it here and introduce it in the following articles.

Memory management

The garbage collector is responsible for the memory management of Java. Automatic reference counting (ARC) is introduced into OC, and memory management is decided by the compiler. Reference count is that each object has a counter. If the object continues to survive, the counter increments its reference count and decrements the reference count after use. If the count becomes 0, the object can be released. The NSObject protocol declares that the Retain, release, and autorelease methods are used to manipulate counters, which are increment, decrement, and automatic release operations, respectively, and all objects are working objects of the collector.

ARC: automatic reference counting, compiler automatically generates retain/release

MRC: manually manage reference counting, used in older versions

Autoreleasepool: delayed automatic release

Strong/weak/assgin best practices

Basic type: assgin

Delegate- > week

Collections and block use copy

Others use strong

Self in block breaks circular references with weak.

Is it helpful for you to read the above content? If you want to know more about the relevant knowledge or read more related articles, please follow the industry information channel, thank you for your support.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report