C/OC/JS-代码备忘

一、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进入main之前调用)- 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;
}

Class / Protocol 获取方法与属性

判断是否声明协议,以及实现方法

- (void)verifyFunction {
    // 是否符合协议 & 是否响应方法
    [NSArray conformsToProtocol:@protocol(NSObject)];
    [NSArray respondsToSelector:@selector(isEqual:)];
}

Class获取方法、属性与协议

- (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);
}

Protocol获取方法、属性

- (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);
}

Category属性懒加载

@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   // 协议列表
}

Crash分析-内存地址

内存地址:16进制,Mach-O原始的内存地址。
内存地址 = Hex(偏移地址的十进制 - 偏移量)

打开Hopper Disassembler,导入Mach-O文件,通过偏移量锁定具体的位置。

利用Hopper进行基于汇编的iOS-Crash栈分析

iOS系统版本Frameworks路劲:~/Library/Developer/Xcode/iOS DeviceSupport/16.1.2 (20B110) arm64e/Symbols/System/Library/Frameworks/UIKit.framework 

文件内存映射mmap 

mmap-内存映射,最常用到的场景是MMKV(mmap key value)-MMKV在iOS中的应用,其次用到的是日志打印。通过系统提供的内存映射储存数据,可以避免APP被杀掉 | crash导致数据丢失。

mmap将磁盘上文件的地址信息与进程用的虚拟逻辑地址进行映射,建立映射的过程与普通的内存读取不同:正常的是将文件拷贝到内存,mmap只是建立映射而不会将文件加载到内存中。

iOS的文件内存映射mmap

自旋锁与互斥锁

各种锁的性能开销

互斥锁:调用者判断保持者是否可用,不可用就终止,互斥锁会引起调用者休眠。

  • pthread_mutex
  • NSLock
  • NSRecursiveLock
  • NSConditionLock
  • @synchronized

自旋锁:调用者会一直循环判断保持者是否已释放,若已释放执行下一个锁,自旋锁不会引起调用者休眠,但较为消耗CPU资源(原子操作)。

  • atomic
  • dispatch_semaphore_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;

为服务器下发的证书关联域名,实现cer证书或公钥的校验逻辑

- (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;
}

Masonry应用

mas_makeConstraints的使用必须在addSubView之后,否则会触发断言。

mas_equalTo:入参可以是id对象,也可以是数字类型,入参为id对象就类似于equalTo,为数字类型表示固定的像素值,或这是相对偏移量

[subView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.bottom.mas_equalTo(-10); 
    /* 等价于:make.bottom.equalTo(view).offset(-10); */

    make.height.mas_equalTo(100);
}];

equalTo(id):view-参照视图,viewAttribute-参照视图约束值,number-固定像素/相对偏移量

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:实现view-model的双向绑定;

/// 双向绑定
RACChannelTo(_textField, text) = RACChannelTo(_model, title);

2. RACObserve:订阅属性的变化信号、实现model-view的单向绑定;

/// 订阅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);

 监听Notification通知事件

[[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"kNotificationLogin" object:nil] subscribeNext:^(NSNotification* notification) {
    NSLog(@"notification_name: %@", notification.name);
}];

3. RACSubject:订阅信号、发送信号,用来代替或者增强原生的delegate与block能力;

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];

获取bundle对象

NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"app-image"ofType:@"bundle"];
NSBundle *resourceBundle = [NSBundle bundleWithPath:bundlePath];

图片MD5的平台差异

iOS图片元数据添加了头信息,导致元数据MD5签名和其他平台不一致。

三、JS

构造函数

声明构造函数的属性与方法

function MyClass() {
  this.myProperty = "it is a property!";
  this.call = function(parameter) {
    // call方法实现
    console.log(parameter);
  };
}

使用prototype实现构造函数的继承

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();

使用prototype实现构造函数的扩展方法

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: {}}));

猜你喜欢

转载自blog.csdn.net/z119901214/article/details/122344654