Objective-C 2.0语言基础
1981年,Brad Cox和Tom Love设计了Objective-C语言,在C语言的基础进行了扩展(即:向下兼容C语言),添加了面向对象编程、动态类型与反射
1988年,NeXT计算机公司获得了Objective-C(wiki,chs)语言的授权,并发展了Objective-C的语言库和开发环境。并在1989年9月18日发布了Objective-C 1.0
在2006年7月苹果全球开发者会议中,Apple宣布了Objective-C 2.0的发布,并在2007年10月在Mac OS X 10.5 Leopard中发布了包含Objective-C 2.0的编译器
在2012年7月25日,Mac OS X 10.8 Mountain Lion和iOS5中引入了ARC
oc语言特点
与c++相比,oc有如下特点:
(1) 大小写敏感
(2) 不支持命名空间
(3) 不支持运算符重载
(4) 单一继承(不支持多重继承)
(5) 使用动态运行时类型,而且所有的方法都是函数(因此不支持函数inline)
注释
// Hello Objective C 2.0! 行注释 /* Hello Objective C 2.0! 块注释 Hello Objective C 2.0! */
预处理
#import <Foundation/Foundation.h> // 等价于c/c++中的#include
前向声明
告诉编译器「有这个类型存在」,但不暴露它的定义。用于打破循环依赖、减少头文件耦合、加快编译
@class FMetalShaderPipeline; // 类的前向声明 @protocol MTLDeviceExtensions <MTLDevice> // protocol的前向声明
基础数据类型
| 类型 | 说明 | 占用字节数 | 示例 |
|---|---|---|---|
char |
字符型 | 1字节 | char c = 'a'; |
unsigned char |
无符号字符型 | 1字节 | |
short |
短整型 | 2字节 | |
unsigned short |
无符号短整型 | 2字节 | |
int |
整型 | 4字节 | int i = 10; |
unsigned int |
无符号整型 | 4字节 | |
long |
长整型 | 4字节/8字节 | |
unsigned long |
无符号长整型 | 4/8字节 | |
long long |
更长的整型 | 8字节 | |
unsigned long long |
无符号长长整型 | 8字节 | |
float |
单精度浮点型 | 4字节 | float f = 1.23f; |
double |
双精度浮点型 | 8字节 | double d = 3.1415; |
bool |
C99标准bool类型 | 1字节 | bool b = true; |
注1:Objective-C 基础数据类型全部兼容C语言
注2:C99 新增 bool,需要 #include <stdbool.h>,一般在纯C代码中
常见OC类型
| OC类型 | 对应的C类型 | 说明 |
|---|---|---|
NSUInteger |
无符号整型 |
NSUInteger BytesWritten = 0; mtlpp::TextureDescriptor Desc; |
NSInteger |
整型 |
|
CGFloat |
浮点型 |
|
BOOL |
布尔型 |
BOOL flag = YES; // YES(1) NO(0) |
NSNumber |
用于对int、float、bool、NSInteger、CGFloat、BOOL等装箱 |
NSNumber *num = [NSNumber numberWithInt:42]; int intValue = [num intValue]; // 将NSNumber转换为int NSNumber *num2 = [NSNumber numberWithBool:YES]; NSNumber *num4 = [NSNumber numberWithInteger:1]; float QualityF = 3.0f; NSNumber *num6 = @42; // 字面量 |
NSValue |
包装struct等非对象类型 | CGRect/CGPoint/CGSize/NSRange是值类型(struct),用 NSValue对它们进行装箱 |
NSData |
二进制数据 | |
NSNull |
NSNull *null = [NSNull null]; // 集合中表示"空" | |
NSDate |
日期时间 | NSDate *date = [NSDate date]; |
NSURL |
URL链接 | NSURL *url = [NSURL URLWithString:@"https://a.com"]; |
NSError |
错误信息 | NSError *error; |
NSArray |
数组(不可变) | NSArray *arr = @[@1, @2, @3]; |
NSMutableArray |
数组(可变) |
// 空数组 // 预设容量(性能优化提示,非硬限制) // 用已有内容初始化 // 由不可变数组转可变 注:@[] 本身是不可变的
/*** 添加元素 ***/ NSMutableArray *arr = [NSMutableArray array]; [arr addObject:@"apple"]; // 尾部添加一个 [arr insertObject:@"first" atIndex:0]; // 指定位置插入 NSLog(@"%@", arr); // 数组arr现在内容为:(first, apple, b, c) 注:打印log会进行换行
/*** 删除元素 ***/ NSMutableArray *arr = [@[@"a", @"b", @"c", @"d", @"b"] mutableCopy]; [arr removeObject:@"b"]; // 删除所有等于 @"b" 的元素
/*** 修改与替换元素 ***/ NSMutableArray *arr = [@[@"a", @"b", @"c"] mutableCopy]; // 替换指定位置的元素 // 交换两个位置
/*** 访问元素 ***/ NSMutableArray *arr = [@[@"a", @"b", @"c"] mutableCopy]; NSUInteger count = arr.count; // 元素个数:3 BOOL has = [arr containsObject:@"b"]; // 是否包含:YES
// 遍历数组 NSMutableArray *arr = [@[@"a", @"b", @"c"] mutableCopy]; // 方式一:快速枚举(最常用) // 方式二:带下标 |
NSDictionary |
字典(不可变) | NSDictionary *dict = @{@"key": @"value"}; |
NSMutableDictionary |
字典(可变) | NSMutableDictionary *mdict = [NSMutableDictionary dictionary]; |
NSSet |
无序不重复集合(不可变) | |
| NSMutableSet | 无序不重复集合(可变) | |
id |
通用对象指针 |
id a = @100; // 整数字面量
NSNumber *num = @3.14; NSInteger i = [(NSNumber *)obj integerValue]; |
注1:基础类型都是值类型,如int、float、bool、NSInteger、CGFloat、BOO等并非对象,不能直接用在 NSArray 等集合里,需用“包装类”进行包装,才能存入OC容器(装箱成NSNumber)
注2:strcut也是值类型,需要装箱成 NSValue,才能存入OC容器
注3:尽量用标准C基本类型,而不是NSInteger、NSUInteger、CGFloag、BOOL等。使用NSInteger、NSUInteger等主要是为了便于兼容 32位/64位系统
| 类型 | 64 位平台实际类型 | 32 位平台 |
|---|---|---|
NSInteger |
long |
int |
NSUInteger |
unsigned long |
unsigned int |
CGFloat |
double |
float |
BOOL |
bool |
signed char |
字符串
| 基础 C 字符串 | char * | const char* MaxFrequencyString = "cpuinfo_max_freq"; |
| C 字符数组 | char str[6] |
char str[6] = "hello"; // 等价于char str[6] = {'h', 'e', 'l', 'l', 'o', '\0'}; char fruits[3][10] = { "apple", "banana", "pear" }; |
| OC 字符串对象(不可变) | NSString * | NSString *str = @"Hello World!"; |
| OC 字符串对象(可变) | NSMutableString * | NSMutableString *mstr = [NSMutableString stringWithString:@"hi"]; |
注:NSString和NSMutableString都是引用类型
枚举类型
OC 支持 C-style 枚举
typedef enum { MyTypeA, MyTypeB } MyType;
NSEnumerator引用类型
作为枚举器(迭代器)用于遍历集合(数组、字典、集合等)中的元素
它是OC早期的遍历方式,核心方法只有一个:nextObject——每次返回下一个元素,取完后返回 nil
现代开发更常用快速枚举(for...in),它内部也是基于枚举协议,语法更简洁、性能更好
NSArray *arr = @[@"a", @"b", @"c"]; // NSEnumerator 写法 NSEnumerator *e = [arr objectEnumerator]; id obj; while ((obj = [e nextObject])) { NSLog(@"%@", obj); } // 快速枚举写法(推荐,等效但更简洁) for (id obj in arr) { NSLog(@"%@", obj); }
闭包(block)
Block 是Objective-C 的闭包:一段可以像对象一样传递的代码,同时能捕获定义时所在作用域的变量。在C++中叫做lamada表达式
本质上它是一个结构体对象(__block_impl),包含函数指针 + 捕获的变量副本,所以它能被存储、传参、放进数组、异步延后执行
形式如下:
返回值类型 (^变量名)(参数类型列表) = ^返回值类型(参数列表) { 代码 };
示例:
// 无参无返回 void (^sayHi)(void) = ^{ // 对应c++的void sayHi(void)函数 NSLog(@"hi"); }; sayHi(); // 调用 // 有参有返回 NSInteger (^sum)(NSInteger, NSInteger) = ^NSInteger(NSInteger a, NSInteger b) { // 对应c++的NSInteger sum(NSInteger a, NSInteger b)函数 return a + b; }; NSLog(@"%ld", (long)sum(2, 3)); // 5 // 返回值类型可省略,由编译器推断 NSInteger (^sum2)(NSInteger, NSInteger) = ^(NSInteger a, NSInteger b) { return a + b; };
用 typedef 让签名可读
typedef void (^CompletionBlock)(NSData * _Nullable data, NSError * _Nullable error); @property (nonatomic, copy) CompletionBlock completion; - (void)fetchWithCompletion:(CompletionBlock)completion; // 对应c++的void fetchWithCompletion(CompletionBlock completion)函数
变量捕获规则
| 变量类型 | 行为 |
|---|---|
| 局部变量 | 值拷贝,Block 内只读,改不了 |
__block 局部变量 |
变量被搬到堆上,Block 内可读可写,外部同步可见 |
全局变量 / static |
直接访问,可读可写 |
实例变量 _ivar |
隐式捕获 self(循环引用来源) |
示例说明
NSInteger a = 10; void (^b1)(void) = ^{ NSLog(@"%ld", (long)a); // 10,捕获的是定义时的快照 // a = 20; // ❌ 编译错误:Variable is not assignable }; a = 99; b1(); // 仍然输出 10 __block NSInteger c = 10; void (^b2)(void) = ^{ c = 20; // ✅ 可修改 }; b2(); NSLog(@"%ld", (long)c); // 20,外部也变了
对象类型的局部变量不需要 __block 就能调方法(因为改的是对象内容,不是指针本身)
NSMutableArray *arr = [NSMutableArray array]; void (^add)(void) = ^{ [arr addObject:@1]; // ✅ 不需要 __block // arr = [NSMutableArray new]; // ❌ 要重新赋值指针才需要 __block };
异步回调示例
// NetworkManager.h typedef void (^RequestCompletion)(id _Nullable result, NSError * _Nullable error); @interface NetworkManager : NSObject - (void)GET:(NSString *)url completion:(RequestCompletion)completion; @end // NetworkManager.m - (void)GET:(NSString *)url completion:(RequestCompletion)completion { NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:url] completionHandler:^(NSData *data, NSURLResponse *resp, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ if (error) { if (completion) completion(nil, error); // 必须判空! return; } id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; if (completion) completion(json, nil); }); }]; [task resume]; } // 调用处,逻辑集中,可读性远好于 delegate __weak typeof(self) weakSelf = self; [manager GET:@"https://api.example.com/users" completion:^(id result, NSError *error) { __strong typeof(weakSelf) self = weakSelf; if (!self) return; if (error) { [self showError:error]; return; } self.users = result; [self.tableView reloadData]; }];
面向对象编程
C++风格的面向对象编程支持多重继承,并尽可能通过编译期绑定获得更快的执行速度,但默认不支持动态绑定;它还强制所有方法都必须有对应的实现,除非它们是抽象方法
而 Objective-C 的面向对象编程模型建立在向对象实例传递消息之上。在 Objective-C 中,人们不是「调用一个方法」,而是「发送一条消息」。因此允许消息没有实现,方法在运行时才解析到它的实现
NSObject是所有类的基类(直接或间接)
[obj method:argument]; // 对应c++的obj->method(argument) // 会被转换为 objc_msgSend(obj, @selector(method:), argument); /*** objc_msgSend完整的签名为: ***/ id objc_msgSend(id self, SEL op, ...); // 可变参数 // 各参数含义: // self → 接收者 obj(消息的接收对象) // op → 选择器 SEL,即由方法名 method: 生成的 @selector(method:)。实际上编译器不会真的在调用点插入 sel_registerName,而是把选择器字符串放进二进制的 __objc_methname 段,并在 __objc_selrefs 段生成一个引用,运行时加载镜像时统一注册、由该引用直接取到唯一化的 SEL // ... → 后续依次是方法的实参 argument
属性(Property)
Property是Objective-C 2.0的重点特性
@property (nonatomic, strong) NSString *name;
注:编译器默认自动生成名为 _属性名(_name)的成员变量与getter/setter方法,getter名为属性名(name),setter名为set + 首字母大写属性名(setName)
@synthesize关键字
// MyClass.h @interface MyClass : NSObject @property (nonatomic, strong) NSString *name; @end // MyClass.m @implementation MyClass // 不写 @synthesize,编译器自动生成一个隐藏的成员变量_name // 如果写 @synthesize name; 则成员变量名字为name // 如果写 @synthesize name = _customName; 则成员变量名字为_customName - (void)test { _name = @"Tom"; // 默认是 _name } @end
@dynamic关键字
@dynamic 告诉编译器不生成属性对应的成员变量和getter/setter 方法,需要程序员自己来写
// ---------- Person.h ---------- @interface Person : NSObject { NSString *_myName; // 要自己声明一个成员变量 } @property (nonatomic, copy) NSString *name; @end // ----------- Person.m ---------- @implementation Person @dynamic name; // getter/setter全部自己实现 - (NSString *)name { return _myName; } - (void)setName:(NSString *)name { _myName = [name copy]; } @end
要自己声明成员变量,推荐也用下划线开头:
// MyClass.h @interface MyClass : NSObject { int _age; BOOL _sex; // 布尔类型(OC扩展),定义值为YES/NO bool _superskill; } @property (nonatomic, strong) NSString *name; @end
属性访问 vs 成员变量访问
| 表达式 | 语法 | 说明 |
|---|---|---|
self.name |
属性访问 | 走getter/setter |
_name |
成员变量访问 | 直接操作成员变量,更高效 |
注:setter方法里不能用 self.name = xxx,否则递归死循环。用 _name = xxx即可
getter/setter自定义方法名
@property (nonatomic, getter=getName, setter=changeName:) NSString *name; // getter方法名变成getName,setter方法名变成changeName [person changeName:@"Tom"]; // 用自定义 setter NSString *n = [person getName]; // 用自定义 getter // 点语法始终用属性名,编译器会自动映射到自定义方法。变的只是“方法名“,不是“属性名“ person.name = @"Jerry"; // 底层调用 changeName NSString *m = person.name; // 底层调用 getName
属性常用修饰符
| 修饰符 | 含义 |
| strong / retain | 强引用(持有对象) |
| weak | 弱引用(不持有,避免循环引用,如 delegate) |
| copy | 拷贝一份(NSString/Block 常用) |
| assign | 基本类型(int/BOOL 等) |
| atomic | 原子,即在访问时会加锁以避免多线程同时访问同一对象。属性默认是atomic的 |
| nonatomic | 非原子,性能高 |
| readonly | 只读 |
属性可以被声明为“readonly”,即只读的
也可以提供储存方法包括“assign”,“copy”或“retain”(简单的赋值、复制或增加1引用计数)
属性默认是atomic(原子)的,即在访问时会加锁以避免多线程同时访问同一对象,也可以将属性声明为nonatomic(非原子)的,避免产生锁
示例说明
// --------- Person.h --------- #import <Foundation/Foundation.h> @interface Person : NSObject @property (nonatomic, copy) NSString *name; // 属性 @property (nonatomic, assign) NSInteger age; - (void)sayHello; // 实例方法 + (instancetype)personWithName:(NSString *)name; // 类方法 @end // --------- Person.m --------- #import "Person.h" @implementation Person - (void)sayHello { NSLog(@"Hi, I'm %@, age %ld", self.name, (long)self.age); } + (instancetype)personWithName:(NSString *)name { Person *p = [[Person alloc] init]; p.name = name; return p; } @end
interface(接口)
对应C++/Java/C#其他编程语言的类(class)的概念
.h:类型声明,头文件
.m:Objective-C源文件后缀,实现(implementation)文件,可混编 C/OC
.mm:Objective-C++源文件后缀,实现(implementation)文件,可混编 C++/OC
#import <Foundation/Foundation.h> // 类的声明 @interface Fraction : NSObject //@interface用于描述类及其成员变量和成员方法 { int numerator; int denominator; } -(void) print; // -开头表示为实例方法,+开头表示为类方法 等价于:void print(); -(void) SetNumerator: (int) n; // 开头()中是返回值类型 等价于:void SetNumerator(int n); -(void) SetDenominator: (int) d; // 冒号后为函数参数 等价于:void SetDenominator(int d); -(void) SetNumerator: (int) n, Denominator:(int) d; // 第2个参数带参数名 等价于:void SetNumeratorDenominator(int n, int d); -(void) SetNumeratorDenominator: (int) n, :(int) d; // 第2个参数不带参数名 等价于:void SetNumeratorDenominator(int n, int d); @end // @interface的结尾 // 类的实现 @implementation Fraction //@implementation包括实现这些成员方法的实际代码 -(void) print { NSLog(@"%i/%i", numerator, denominator); } -(void) SetNumerator: (int) n { numerator = n; } -(void) SetDenominator: (int) d { denominator = d; } -(void) SetNumerator: (int) n, Denominator:(int) d { numerator = n; denominator = d; } -(void) SetNumeratorDenominator: (int) n, :(int) d { numerator = n; denominator = d; } @end // @implementation的结尾 int main(int argc, char* argv[]) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; Fraction* myFraction; // 创建一个Fraction实例 myFraction = [Fraction alloc]; // 调用Fraction类的静态函数alloc分配一个Fraction对象 myFraction = [myFraction init]; // 名为myFraction实例调用init成员函数 [myFraction SetNumerator: 1]; // 名为myFraction实例传入参数1并调用SetNumerator成员函数 [myFraction SetDenominator: 3]; // 名为myFraction实例传入参数3,并调用SetDenominator成员函数 [myFraction SetNumerator: 2 Denominator:5]; // 名为myFraction实例传入参数2, 5,并调用SetDenominator:Denominator成员函数 [myFraction SetNumeratorDenominator: 6 : 8]; // 名为myFraction实例传入参数6, 8,并调用SetNumeratorDenominator成员函数 [myFraction print]; // 名为myFraction实例调用print成员函数 [myFraction release]; // 名为myFraction实例调用release成员函数,释放自己占用的内存 [pool drain]; // 名为pool实例调用成员函数drain return 0; }
更复杂的情况:
#ifndef MyAppLifecycle_h #define MyAppLifecycle_h #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #include "MyAppLifecycleObserver.h" @interface MyAppLifecycle : NSObject // static MyAppLifecycle* sharedInstance() + (MyAppLifecycle*) sharedInstance; // 静态成员 // void addObserver(NSObject<MyAppLifecycleObserver>* observer) - (void) addObserver:(NSObject<MyAppLifecycleObserver>*) observer; // void removeObserver(NSObject<MyAppLifecycleObserver>* observer) - (void) removeObserver:(NSObject<MyAppLifecycleObserver>*) observer; // BOOL application_didFinishLaunchingWithOptions(UIApplication* application, NSDictionary* launchOptions) - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions; // BOOL handleOpenURL(NSURL* url) - (BOOL)handleOpenURL:(NSURL *)url; // BOOL application_openURL_options(UIApplication* app, NSURL* url, NSDictionary<UIApplicationOpenURLOptionsKey,id>* options) - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options; // BOOL application_openURL_sourceApplication_annotation(UIApplication* application, NSURL* url, NSString* sourceApplication, id annotation) - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation; // void applicationDidEnterBackground(UIApplication* application) - (void)applicationDidEnterBackground:(UIApplication *)application; // void applicationWillEnterForeground(UIApplication* application) - (void)applicationWillEnterForeground:(UIApplication *)application; // void applicationDidBecomeActive(UIApplication* application) - (void)applicationDidBecomeActive:(UIApplication*)application; // void applicationWillResignActive(UIApplication* application) - (void)applicationWillResignActive:(UIApplication*)application; // void applicationWillTerminate(UIApplication* application) - (void)applicationWillTerminate:(UIApplication*)application; // void applicationDidReceiveMemoryWarning(UIApplication* application) - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application; // void application_didRegisterForRemoteNotificationsWithDeviceToken(UIApplication* application, NSData* deviceToken) - (void) application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken; // void application_didFailToRegisterForRemoteNotificationsWithError(UIApplication* application, NSError* error) - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error; // void application_didReceiveRemoteNotification_fetchCompletionHandler(UIApplication* application, NSDictionary* userInfo, void (^)(UIBackgroundFetchResult) completionHandler) - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler; // void application_didReceiveRemoteNotification:(UIApplication* application, NSDictionary* userInfo); - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo; #endif /* MyAppLifecycle_h */ // -------------------------- 测试函数 ------------------------------ // void OnOpenURL(UIApplication* application, NSURL* url, NSString* sourceApplication, id annotation) { NSLog(@"OnOpenURL"); // GCD(Grand Central Dispatch)方法,通过向主线程队列dispatch一个block块,使block里的方法可以在主线程中执行 dispatch_async(dispatch_get_main_queue(), ^ { // MyAppLifecycle::sharedInstance().application_openURL_sourceApplication_annotation(application, url, sourceApplication, annotation) [[MyAppLifecycle sharedInstance] application:application openURL:url sourceApplication:sourceApplication annotation:annotation]; }); }
UE引擎中的示例
UnrealEngine\Engine\Source\Runtime\Core\Public\IOS\IOSAsyncTask.h
#pragma once #include "CoreTypes.h" #include "CoreTypes.h" #import <Foundation/Foundation.h> @interface FIOSAsyncTask : NSObject // 声明FIOSAsyncTask类 { @private /** Whether or not the task is ready to have GameThread callback called (set on iOS thread) */ int32 bIsReadyForGameThread; // 成员变量 } /** Extra data for this async task */ @property (retain) id UserData; // @property 编译会帮我们生成对应的 setter、getter 方法,可通过self.UserData来获取和修改UserData /** * Code to run on the game thread when the async task completes * @return true when the task is complete, and it will be pulled from the list of tasks and destroyed */ @property (copy) bool (^GameThreadCallback)(void); // @property 编译会帮我们生成对应的 setter、getter 方法,GameThreadCallback为一个bool func(void)的函数指针 /** * Create an async task object, set the block, and mark it as ready to be processed on main thread * For advanced uses (ie, setting UserData or delaying when it's ready for game thread, use the usual [[alloc] init] pattern * * Note, this doesn't return an AsyncObject so no one is tempted to use it - once it's ready for game thread, it's dangerous to touch it * This is callable on any thread, not just main thread */ + (void)CreateTaskWithBlock:(bool (^)(void))Block; // +表示类方法 对应c++方法形式为:void CreateTaskWithBlock(fp Block) 注:其中typedef bool (*fp)(void) /** * Mark that the task is complete on the iOS thread, and now the * GameThread can be fired (the Task is unsafe to use after this call) */ - (void)FinishedTask; // -表示实例方法 对应c++方法形式为:void FinishedTask() /** * Tick all currently running tasks */ + (void)ProcessAsyncTasks; // +表示类方法 对应c++方法形式为:void ProcessAsyncTasks() @end
UnrealEngine\Engine\Source\Runtime\Core\Private\IOS\IOSAsyncTask.cpp
#include "IOS/IOSAsyncTask.h" #include "HAL/PlatformAtomics.h" @implementation FIOSAsyncTask @synthesize UserData; @synthesize GameThreadCallback; /** All currently running tasks (which can be created on iOS thread or main thread) */ NSMutableArray* RunningTasks; /** * Static class constructor, called before any instances are created */ + (void)initialize { // create the RunningTasks object one time RunningTasks = [[NSMutableArray arrayWithCapacity:4] retain]; } /** * Initialize the async task */ - (id)init { self = [super init]; // add ourself to the list of tasks @synchronized(RunningTasks) { [RunningTasks addObject:self]; } // return ourself, the constructed object return self; } + (void)CreateTaskWithBlock:(bool (^)(void))Block { // create a task, and add it to the array FIOSAsyncTask* Task = [[FIOSAsyncTask alloc] init]; // set the callback Task.GameThreadCallback = Block; // safely tell the game thread we are ready to go [Task FinishedTask]; } - (void)FinishedTask { FPlatformAtomics::InterlockedIncrement(&bIsReadyForGameThread); } /** * Check for completion * * @return TRUE if we succeeded (the completion block will have been called) */ - (bool)CheckForCompletion // 该实例函数,没有写在头文件中,对外不可见,只能在当前cpp用使用 { // handle completion if (bIsReadyForGameThread) { // call the game thread block if (GameThreadCallback) { if (GameThreadCallback()) { // only return true if the callback says it's complete return true; } } else { // if there isn't a callback, then just return TRUE to remove the // task from the queue return true; } } // all other cases, we are not complete return false; } /** * Tick all currently running tasks */ + (void)ProcessAsyncTasks { FIOSAsyncTask* CurrentTask = nil; // grab one out of the queue to process outside of the lock @synchronized(RunningTasks) // 每次取出一个任务来执行 { if ([RunningTasks count] > 0) { CurrentTask = [RunningTasks objectAtIndex:0]; [RunningTasks removeObjectAtIndex:0]; } } if (CurrentTask != nil) { if ([CurrentTask CheckForCompletion]) // 执行任务 { // release the object count [CurrentTask release]; } else { // if it's not done, but it back in the list @synchronized(RunningTasks) { [RunningTasks addObject:CurrentTask]; } } } } /** * Application destructor */ - (void)dealloc { [GameThreadCallback release]; self.UserData = nil; [super dealloc]; } @end
UnrealEngine\Engine\Source\Runtime\ApplicationCore\Private\IOS\IOSAppDelegate.cpp
- (void)applicationDidEnterBackground:(UIApplication *)application { /* Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. */ // fix for freeze on tvOS, moving to applicationDidEnterBackground. Not making the changes for iOS platforms as the bug does not happen and could bring some side effets. #if PLATFORM_TVOS [self ToggleSuspend:true]; #endif FEmbeddedCommunication::KeepAwake(TEXT("Background"), false); [FIOSAsyncTask CreateTaskWithBlock : ^ bool(void) { // the audio context should resume immediately after interrupt, if suspended FAppEntry::ResetAudioContextResumeTime(); FCoreDelegates::ApplicationWillEnterBackgroundDelegate.Broadcast(); FEmbeddedCommunication::AllowSleep(TEXT("Background")); return true; }]; }
协议(protocol)
对应Java/C#其他编程语言接口(interface)的概念。与Java/C#一样,oc支持单一class多protocol继承
例如UE4的UE4AppDelegate从NSObject类和NSApplicationDelegate, NSFileManagerDelegate协议上派生
// UnrealEngine\Engine\Source\Runtime\Launch\Private\Mac\LaunchMac.cpp @interface UE4AppDelegate : NSObject <NSApplicationDelegate, NSFileManagerDelegate> // 等价于c++的class UE4AppDelegate : NSObject, NSApplicationDelegate, NSFileManagerDelegate 其中NSApplicationDelegate和NSFileManagerDelegate为protocol { #if WITH_EDITOR NSString* Filename; bool bHasFinishedLaunching; #endif
示例代码:
// Flyable.h @protocol Flyable <NSObject> // Flyable协议从NSObject派生 @required // 默认就是 @required,不实现会警告 - (void)takeOff; - (void)land; @optional // 可选,不实现不警告,但调用前要检查 - (CGFloat)maxAltitude; @property (nonatomic, assign) BOOL isFlying; // 协议也能声明属性 + (NSString *)category; // 也能声明类方法 @end // Bird.h #import "Flyable.h" @interface Bird : NSObject <Flyable> // 采用协议,必须 #import 协议定义 @end // Bird.m @implementation Bird @synthesize isFlying; // 协议里的属性需手动 synthesize - (void)takeOff { NSLog(@"扑翅膀"); } - (void)land { NSLog(@"落枝头"); } @end // Plane.h 完全不同的类,也能遵守同一协议 #import "Flyable.h" @interface Plane : NSObject <Flyable> @end // Plane.m @implementation Plane @synthesize isFlying; // 协议里的属性需手动 synthesize - (void)takeOff { NSLog(@"起飞"); } - (void)land { NSLog(@"着陆"); } @end
调用方只面向协议(protocol) 编程:
NSMutableArray<id<Flyable>> *fleet = [NSMutableArray array]; [fleet addObject:[Bird new]]; [fleet addObject:[Plane new]]; for (id<Flyable> f in fleet) { [f takeOff]; // 不关心具体是鸟还是飞机 if ([f respondsToSelector:@selector(maxAltitude)]) { // optional 必须先检查 NSLog(@"%f", [f maxAltitude]); } }
Delegate 模式示例
下载器完成后要通知别人,但它不需要知道对方是谁
// Downloader.h @class Downloader; @protocol DownloaderDelegate <NSObject> @required - (void)downloader:(Downloader *)d didFinishWithData:(NSData *)data; @optional - (void)downloader:(Downloader *)d didFailWithError:(NSError *)error; - (void)downloader:(Downloader *)d didUpdateProgress:(CGFloat)progress; @end @interface Downloader : NSObject @property (nonatomic, weak) id<DownloaderDelegate> delegate; // weak!避免循环引用 - (void)start; @end // Downloader.m @implementation Downloader - (void)start { // ... 下载完成后 NSData *data = ...; if ([self.delegate respondsToSelector:@selector(downloader:didFinishWithData:)]) { [self.delegate downloader:self didFinishWithData:data]; } } @end // ViewController.m @interface ViewController () <DownloaderDelegate> // 私有采用,不暴露在 .h @end @implementation ViewController - (void)load { Downloader *d = [Downloader new]; d.delegate = self; [d start]; } - (void)downloader:(Downloader *)d didFinishWithData:(NSData *)data { NSLog(@"收到 %lu 字节", (unsigned long)data.length); } @end
开发常用功能示例
放到gcb线程执行
GCD(Grand Central Dispatch)库是纯C语言实现的,是非常高效的多线程开发方式
在GCD中,开发者只需要做两件事:①定义任务 ②将任务添加到队列中。其核心就是dispatch队列和任务
1.主线程队列(Main Queue):提交的任务将会在主线程完成
可以通过dispatch_get_main_queue()来获得
主队列就是主线程,它是一个串行队列,在iOS中只有主线程才能拥有权限向渲染服务提交图层信息,完成图形显示工作。所以和UI相关操作,必须在主线程执行
2.全局并发队列(Global Queue):全局并发队列由整个进程共享,有高、中(默认)、低、后台四个优先级
可以通过dispatch_get_global_queue()来获得
派发到主线程上执行
int64 AvailablePhysical = 0; dispatch_block_t blockFunc = nil; if (@available(iOS 14.3, *)) { blockFunc = ^{ AvailablePhysical = os_proc_available_memory(); } } if (blockFunc != nil) { if ([NSThread isMainThread]) { blockFunc(); } else { dispatch_async(dispatch_get_main_queue(), blockFunc); } }
注1:dispatch_block_t是一个函数指针,形如typedef void (^dispatch_block_t)(void);

注2:ios 将一个函数在主线程执行的4种方法 https://www.cnblogs.com/linusflow/p/8541707.html
派发到线程池的线程上执行
apm组件在获取温度状态时,系统会加锁,此时温度状态又发生了变化,此时锁还没释放,UE在通知中不能直接获取温度状态,需要派发到gcb线程中异步获取
- (void)thermalStateDidChange { MatrixDebug(@"thermal state did change"); if (@available(iOS 11.0, *)) { // On iOS 15.0.2, Foundation.framework might post ThermalStateDidChangeNotification from -[NSProcessInfo thermalState], // recursively calling -[NSProcessInfo thermalState] in the notification's observer could cause a crash. // Dispatch it as a workaround. FB9802727 to Apple. Already fixed on iOS 15.2. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSProcessInfoThermalState currentThermalState = [[NSProcessInfo processInfo] thermalState]; if (currentThermalState > g_thermalState) { BM_SAFE_CALL_SELECTOR_NO_RETURN(self.delegate, @selector(onBlockMonitorThermalStateElevated:), onBlockMonitorThermalStateElevated:self); } g_thermalState = currentThermalState; }); } }
更多信息可查看:https://juejin.cn/post/6892426259784335367
判断os版本
BOOL IsiOS14Plus = NO; BOOL IsiOS16Plus = NO; if (@available(iOS 14, *)) { IsiOS14Plus = YES; } if (@available(iOS 16, *)) { IsiOS16Plus = YES; } if (@available(iOS 12.0, macOS 10.13, *)) { } if (@available(iOS 13, tvOS 13, macOS 10.15, *)) { }
查询麦克风权限
int QeuryMicPermission() { [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) { if (IsInGameThread()) { // DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FIOSPermissionDynamicDelegate, bool, status); // FIOSPermissionDynamicDelegate OnPermissionsGrantedDynamicDelegate OnPermissionsGrantedDynamicDelegate.Broadcast(granted != 0); } else { AsyncTask(ENamedThreads::GameThread, [granted]() { OnPermissionsGrantedDynamicDelegate.Broadcast(granted != 0); }); } }]; return 0; }
浙公网安备 33010602011771号