1.C
メモリの適用と解放
- malloc後のメモリは初期化されていないため、実装するにはmemsetを呼び出す必要があります。calloc は必要ありません (alloc + init)
- realloc は新しいメモリを申請し、内容をコピーした後、元のメモリ空間を解放します。
void allocInit() {
char *str = malloc(sizeof(char)*5);
memset(str, 0, sizeof(char)*5);
/* 等价于:char *str = calloc(5, sizeof(chart)); */
strcpy(str, "abcde"); // 拷贝:abcde
str = (char *)realloc(str, sizeof(char)*10);
strcat(str, "fghij"); // 拼接:fghij
free(str);
str = NULL; // 释放内存并赋为NULL,防止野指针
}
int | char 配列のプレースホルダー
int nums[10];
char str[10];
数组未填充会有占位符
int数组占位符:0 | 随机数字
char数组占位符:'\0'
構造体struct の使用 (struct 型 + struct ポインタ)
typedef struct sPersonInfo {
char name[20];
char telNumber[20];
int age;
} PersonInfo;
typedef struct sList {
PersonInfo infos[20];
int count;
} List;
void test() {
PersonInfo personInfo;
strcpy(personInfo.telNunber, "13676399598");
strcpy(personInfo.name, "刘帅");
personInfo.age = 27;
list* list = (List *)malloc(sizeof(List));
memset(list, 0, sizeof(List));
list->infos[0] = personInfo;
list->count = 1;
free(list);
list = NULL;
}
Xcode プロジェクトへの参照
実際に参照したコード例 (demo.h/demo.c)
#ifdef __cplusplus
extern "C" {
#endif
extern int demo_main(int argc, char * argv[]);
#ifdef __cplusplus
}
#endif
#include "demo.h"
二、OC
APP コンストラクターとデストラクター
__attribute__((constructor)) コンストラクター宣言 (APP がメインに入る前に呼び出されます) - iOS コールド スタート最適化のためのモジュール起動項目の自己登録実装
#include <mach-o/dyld.h>
static void dyld_register_func(const struct mach_header *mh, intptr_t vmaddr_slide) {
// 实现模块自注册
}
__attribute__((constructor))
static void load_file(void) {
_dyld_register_func_for_add_image(dyld_register_func);
}
__attribute__((destructor)) デストラクター宣言 (APP プロセスが強制終了される前に呼び出されます)
__attribute__((destructor))
static void exit_app(void) {
// 监听APP被杀掉的频率
}
NSInvocation使用(argument & return)
+ (BOOL)invokeTarget:(id)target
action:(_Nonnull SEL)selector
arguments:(NSArray* _Nullable )arguments
returnValue:(void* _Nullable)result; {
if (target && [target respondsToSelector:selector]) {
NSMethodSignature *signature = [target methodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:target];
[invocation setSelector:selector];
for (NSUInteger i = 0; i<[arguments count]; i++) {
NSUInteger argIndex = i+2;
id argument = arguments[i];
if ([argument isKindOfClass:NSNumber.class]) {
//convert number object to basic num type if needs
NSNumber *num = (NSNumber*)argument;
const char *type = [signature getArgumentTypeAtIndex:argIndex];
// 目前协议方法只需要支持bool、integer与float
if (strcmp(type, @encode(BOOL)) == 0) {
BOOL rawNum = [num boolValue];
[invocation setArgument:&rawNum atIndex:argIndex];
continue;
} else if (strcmp(type, @encode(int)) == 0
|| strcmp(type, @encode(short)) == 0
|| strcmp(type, @encode(long)) == 0) {
NSInteger rawNum = [num integerValue];
[invocation setArgument:&rawNum atIndex:argIndex];
continue;
} else if (strcmp(type, @encode(float)) == 0) {
float rawNum = [num floatValue];
[invocation setArgument:&rawNum atIndex:argIndex];
continue;
}
}
if ([argument isKindOfClass:[NSNull class]]) {
argument = nil;
}
[invocation setArgument:&argument atIndex:argIndex];
}
[invocation invoke];
NSString *methodReturnType = [NSString stringWithUTF8String:signature.methodReturnType];
if (result && ![methodReturnType isEqualToString:@"v"]) { //if return type is not void
if([methodReturnType isEqualToString:@"@"]) { //if it's kind of NSObject
CFTypeRef cfResult = nil;
[invocation getReturnValue:&cfResult]; //this operation won't retain the result
if (cfResult) {
CFRetain(cfResult); //we need to retain it manually
*(void**)result = (__bridge_retained void *)((__bridge_transfer id)cfResult);
}
} else {
[invocation getReturnValue:result];
}
}
return YES;
}
return NO;
}
クラス/プロトコルの取得メソッドとプロパティ
プロトコルを宣言するかどうか、およびその実装方法を決定する
- (void)verifyFunction {
// 是否符合协议 & 是否响应方法
[NSArray conformsToProtocol:@protocol(NSObject)];
[NSArray respondsToSelector:@selector(isEqual:)];
}
クラスの取得方法、プロパティ、およびプロトコル
- (void)classMethods_Propertys_Protocols {
unsigned int count;
Class class = NSClassFromString(@"NSArray");
/* 等价于:[NSArray class] */
// 获取Class的方法
Method *classMethodList = class_copyMethodList(class, &count);
for (int i=0; i<count; i++) {
Method classMethod = classMethodList[i];
NSLog(@"%@", NSStringFromSelector(method_getName(classMethod)));
}
free(classMethodList);
// 获取Class的属性
objc_property_t *classPropertyList = class_copyPropertyList(class, &count);
for (int i=0; i<count; i++) {
objc_property_t classProperty = classPropertyList[i];
NSLog(@"%s", property_getName(classProperty));
}
free(classPropertyList);
// 获取Class的协议
Protocol * __unsafe_unretained *classProtocolList = class_copyProtocolList(class, &count);
for (int i=0; i<count; i++) {
Protocol *protocol = classProtocolList[i];
NSLog(@"%@", NSStringFromProtocol(protocol));
}
free(classProtocolList);
}
プロトコルの取得方法と属性
- (void)protocolMethods_Propertys {
unsigned int count;
Protocol* protocol = NSProtocolFromString(@"NSObject");
/* 等价于:@protocol(NSObject) */
// 获取Protocol的方法
struct objc_method_description *protocolMethodList = protocol_copyMethodDescriptionList(protocol, NO, YES, &count);
for (int i=0; i<count; i++) {
struct objc_method_description protocolMethod = protocolMethodList[i];
NSLog(@"%@", NSStringFromSelector(protocolMethod.name));
}
free(protocolMethodList);
// 获取Protocol的属性
objc_property_t *protocolPropertyList = protocol_copyPropertyList(protocol, &count);
for (int i=0; i<count; i++) {
objc_property_t protocolProperty = protocolPropertyList[i];
NSLog(@"%s", property_getName(protocolProperty));
}
free(protocolPropertyList);
}
カテゴリ属性の遅延読み込み
@interface UIViewController (Category)
@property (nonatomic, strong) NSMutableArray *datas;
@end
static const void *datasKey = &datasKey;
@implementation UIViewController (Category)
- (NSMutableArray *)datas {
NSMutableArray *datas = objc_getAssociatedObject(self, datasKey);
if (!datas) {
datas = [[NSMutableArray alloc] init];
[self setDatas:datas];
}
return datas;
}
- (void)setDatas:(NSMutableArray *)datas {
objc_setAssociatedObject(self, datasKey, datas, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
objc_class 構造体
struct objc_class {
Class isa // 所属类的指针
Class super_class // 指向父类的指针
const char *name // 类名
struct objc_ivar_list *ivars // 成员变量列表
struct objc_method_list **methodLists // 方法列表
struct objc_protocol_list *protocols // 协议列表
}
クラッシュ解析 - メモリアドレス
メモリ アドレス: 16 進数、Mach-O のオリジナル メモリ アドレス。
メモリアドレス = 16 進数 (オフセットアドレスの 10 進数 - オフセット)
Hopper 逆アセンブラーを開き、Mach-O ファイルをインポートし、オフセットを通じて特定の位置をロックします。
Hopper を使用したアセンブリベースの iOS クラッシュ スタック分析
iOS システムバージョンフレームワークパス: ~/Library/Developer/Xcode/iOS DeviceSupport/16.1.2 (20B110) arm64e/Symbols/System/Library/Frameworks/UIKit.framework
ファイル メモリ マッピング mmap
mmap - メモリ マッピング。最も一般的に使用されるシナリオは MMKV (mmap キー値) - iOS での MMKV のアプリケーションで、次にログ出力が続きます。システムが提供するメモリ マップを通じてデータを保存することで、APP の強制終了やクラッシュによるデータ損失を回避できます。
mmap は、ディスク上のファイルのアドレス情報を、プロセスが使用する仮想論理アドレスにマッピングします。マッピングを確立するプロセスは、通常のメモリ読み取りとは異なります。通常、ファイルはメモリにコピーされ、mmap はマッピングを確立するだけです。ファイルをメモリにロードせずに。
スピンロックとミューテックス
ミューテックス: 呼び出し側がホルダーが利用可能かどうかを判断し、利用できない場合は終了し、ミューテックスにより呼び出し側はスリープ状態になります。
- pthread_mutex
- NSロック
- NSRecursiveLock
- NSコンディションロック
- @同期
スピン ロック: 呼び出し元は、ホルダーが解放されたかどうかを判断するためにループを続けます。次のロックを実行するために解放された場合、スピン ロックは呼び出し元をスリープさせませんが、より多くの CPU リソースを消費します (アトミック操作)。
- 原子
- ディスパッチ_セマフォ_t
OSSpinLock(優先度逆転問題あり)
// 读写锁-多读单写 (pthread_rwlock | dispatch_barrier)
pthread_rwlock_t rwLock;
pthread_rwlock_init(&rwLock, NULL);
pthread_rwlock_rdlock(&rwLock);
pthread_rwlock_unlock(&rwLock);
pthread_rwlock_wrlock(&rwLock);
pthread_rwlock_unlock(&rwLock);
pthread_rwlock_destroy(&rwLock);
HTTP リクエストはグローバル プロキシをバイパスします
connectionProxyDictionaryが空のディクショナリに設定され、グローバル プロキシがキャンセルされ、パケット キャプチャ ツールが無効になります。
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.connectionProxyDictionary = @{};
HTTPS 証明書の構成と検証
URLSessionに対応するコールバックメソッドを処理する
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
NSURLCredential *credential = nil;
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// 校验HTTPS服务器证书与域名的一致性,IP直连:域名在转换IP前存于header中,取出'域名'与'证书'进行关联
if ([self evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
credential = [[NSURLCredential alloc] initWithTrust:challenge.protectionSpace.serverTrust];
// 使用证书
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
// 取消身份验证质询,请求失败
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) {
/* 配置P12证书,开启双向认证
credential =[NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
*/
} else {
// 执行默认处理
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
サーバーが信頼できるかどうかを確認し、無効な証明書とローカル証明書の検証を許可するスイッチを追加します。
static BOOL serverTrustIsVaid(SecTrustRef serverTrust) {
SecTrustResultType result;
SecTrustEvaluate(serverTrust, &result);
BOOL isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
return isValid;
}
static BOOL allowInvalidCertificates = YES;
static BOOL SSLPinningCertificate = NO;
static BOOL validatesDomainName = YES;
サーバーが発行した証明書にドメイン名を関連付けて、サーバー証明書または公開キーの検証ロジックを実装します。
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain {
NSMutableArray *policies = [NSMutableArray array];
if (domain) {
if (validatesDomainName) {
// 域名校验
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
// 不进行域名校验
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
} else {
// 没有绑定domain不会进行域名校验
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
if (SSLPinningCertificate == NO) {
// 允许无效证书,或服务器可信任,返回YES
return allowInvalidCertificates || serverTrustIsVaid(serverTrust);
} else if (!allowInvalidCertificates && !serverTrustIsVaid(serverTrust)) {
// 不允许无效证书,且服务器不可信,返回NO
return NO;
}
// 单向认证防止中间人攻击:获取本地cer证书集合,判断是否包含服务器下发的证书(serverTrust),不匹配则取消身份验证质询
NSArray *pinnedCertificates = @[];
NSArray *serverCertificates = @[];
BOOL isContains = NO;
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
if ([pinnedCertificates containsObject:trustChainCertificate]) {
isContains = YES;
break;
}
}
if (isContains) return YES;
/*
NSArray *pinnedPublicKeys = @[];
NSArray *serverPublicKeys = @[];
BOOL isContains = NO;
for (NSData * publicKey in [serverPublicKeys reverseObjectEnumerator]) {
if ([pinnedPublicKeys containsObject:publicKey]) {
isContains = YES;
break;;
}
}
if (isContains) return YES;
*/
return NO;
}
石工用途
mas_makeConstraintsはaddSubViewの後に使用する必要があります。そうでない場合はアサーションがトリガーされます。
mas_equalTo: 入力パラメータは、ID オブジェクトまたは数値タイプにすることができます。入力パラメータは、数値タイプの固定ピクセル値を表す、equalTo に似た ID オブジェクト、または相対オフセットです。
[subView mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(-10);
/* 等价于:make.bottom.equalTo(view).offset(-10); */
make.height.mas_equalTo(100);
}];
qualTo(id): ビュー参照ビュー、viewAttribute 参照ビュー制約値、数値固定ピクセル/相対オフセット
offset: オフセット(CGFloat型)
/* 等价调用示例 */
[subView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.top.bottom.mas_equalTo(0);
/* 等价调用 */
make.edges.mas_equalTo(view);
make.left.right.top.bottom.equalTo(view);
make.left.right.top.bottom.equalTo(@0);
make.bottom.mas_equalTo(-10);
/* 等价调用 */
make.bottom.equalTo(view).offset(-10);
make.bottom.equalTo(@(-10));
make.bottom.equalTo(view.mas_bottom).offset(-10);
}];
/* subView常规布局约束示例 */
[subView mas_makeConstraints:^(MASConstraintMaker *make) {
/* view中有多个subview的情况,边距设置最好是有参照视图,除非subView是最靠近view边沿的位置 */
/* subView上方有topView,不推荐这样使用:make.top.equalTo(@12); */
make.top.equalTo(topView.mas_bottom).offset(12);
make.width.mas_equalTo(140);
make.height.mas_equalTo(48);
make.centerX.mas_equalTo(view.mas_centerX).offset(10);
}];
/* 通过subView约束决定view的高度 */
[subView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.equalTo(view).offset(12);
make.right.equalTo(view).offset(-12);
make.height.mas_equalTo(351);
// 由subView适配view的高度,view不能设置高度,否则后有约束冲突。
make.bottom.equalTo(view).offset(-10);
}];
mas_updateConstraintsを使用して制約を追加/更新する
// 更新约束
[subView mas_updateConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(view).offset(-15);
}];
[view setNeedsUpdateConstraints];
mas_remakeConstraintsを使用して制約を追加/更新/削除する
// 先删除再添加约束
[subView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.left.equalTo(view).offset(20);
make.right.equalTo(view).offset(-20);
make.height.mas_equalTo(335);
make.bottom.equalTo(view).offset(-20);
}];
[view setNeedsUpdateConstraints];
AutoLayout レイアウトの更新: setNeedsUpdateConstraintsフラグでレイアウトを更新する必要がある場合は、updateConstraintsIfNeeded を呼び出してすぐに実行することも、メイン スレッドの mainRunLoop が実行をトリガーするのを待つこともできます。
- (void)setNeedsUpdateConstraints 标记需要进行重新布局
- (void)updateConstraintsIfNeeded 调用此方法,如果有标记为需要重新布局的约束,则立即进行重新布局,内部会调用updateConstraints方法
- (void)updateConstraints 重写此方法,内部实现自定义布局过程
- (BOOL)needsUpdateConstraints 当前是否需要重新布局,内部会判断当前有没有被标记的约束
ReactiveObjC アプリケーション
1. RACChannelTo : ビューモデルの双方向バインディングを実現します。
/// 双向绑定
RACChannelTo(_textField, text) = RACChannelTo(_model, title);
2. RACObserve : 属性の変更シグナルをサブスクライブし、モデルビューの一方向バインディングを実現します。
/// 订阅textField.text的变化信号
[RACObserve(_textField, text) subscribeNext:^(id x) {
}];
/// 订阅textField.text的变化信号
[_textField.rac_textSignal subscribeNext:^(id x) {
}];
/// lable订阅了textField的文本变化信号
RAC(_label, text) = _textField.rac_textSignal;
/// 实现model-view的单向绑定
RAC(_label, text) = RACObserve(self, title);
通知通知イベント をリッスンする
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"kNotificationLogin" object:nil] subscribeNext:^(NSNotification* notification) {
NSLog(@"notification_name: %@", notification.name);
}];
3. RACSubject : シグナルのサブスクライブ、シグナルの送信。元のデリゲートおよびブロック機能を置き換えたり拡張したりするために使用されます。
RACSubject* subject = [RACSubject subject];
/// 订阅信号
[subject subscribeNext:^(id x) {
}];
/// 发送信号
[subject sendNext:nil];
4. RACCommand : データ取得を実現します (シグナルの作成、シグナルのサブスクライブ、実行ステータスのサブスクライブ、例外のサブスクライブ、コマンドの実行、シグナルの送信、例外の送信、シグナルの終了)。
@weakify(self);
RACCommand* command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
/// 创建信号
@strongify(self);
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
/// 执行异步请求
@strongify(self);
[self GET:URLString success:^(NSDictionary* response) {
[subscriber sendNext:response]; /// 发送信号
[subscriber sendCompleted]; /// 结束信号
} failure:^(NSError *error) {
[subscriber sendError:error]; /// 发送异常
[subscriber sendCompleted]; /// 结束信号
}];
return [RACDisposable disposableWithBlock:^{
/// 信号已销毁
}];
}];
}];
/// 订阅每个信号
[command.executionSignals subscribeNext:^(NSDictionary *response) {
}];
/// 订阅completed前的最后一个信号
[command.executionSignals.switchToLatest subscribeNext:^(NSDictionary *response) {
}];
/// 订阅每个异常
[command.errors subscribeNext:^(NSError *error) {
}];
/// 订阅completed前的最后一个异常
[command.errors.switchToLatest subscribeNext:^(NSError *error) {
}];
/// 订阅信号的执行状态
[command.executing subscribeNext:^(NSNumber *boolNum) {
if ([boolNum boolValue]) {
/// 还在执行
} else {
/// 执行结束
}
}];
/// 执行命令
[command executing];
バンドルオブジェクトを取得する
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"app-image"ofType:@"bundle"];
NSBundle *resourceBundle = [NSBundle bundleWithPath:bundlePath];
イメージ MD5 のプラットフォームの違い
ヘッダー情報が iOS 画像メタデータに追加されるため、メタデータの MD5 署名が他のプラットフォームと矛盾します。
3.JS
コンストラクタ
コンストラクターのプロパティとメソッドを宣言する
function MyClass() {
this.myProperty = "it is a property!";
this.call = function(parameter) {
// call方法实现
console.log(parameter);
};
}
プロトタイプを使用してコンストラクターの継承を実装する
function SuperClass() {
this.value = "it is a value!";
let _this = this;
this.setValue = function(value) {
_this.value = value;
console.log(_this.value);
};
}
MyClass.prototype = new SuperClass();
プロトタイプを使用してコンストラクターの拡張メソッドを実装します。
MyClass.prototype.callBack = function(result) {
// callBack方法实现
console.log(this.myProperty);
console.log(result);
};
コンストラクターのインスタンスを作成し、メソッドを実行します。
let instance = new MyClass();
instance.call(JSON.stringify({title: '标题', content: '内容'})); // 内部方法
instance.setValue('lt is a new value!'); // 继承
instance.callBack(JSON.stringify({code: 0, message: 'success', data: {}})); // 扩展
インスタンスにメソッドをマウントし、メソッドを呼び出します
instance.handleMessage = function(message) {
// 处理消息
console.log(message);
};
instance.handleMessage(JSON.stringify({type: 'notify', payload: {}}));