C/OC/JS-code memo

1. C

Memory application and release

  • The memory after malloc is not initialized, you need to call memset to implement, calloc does not need (alloc + init)
  • realloc will apply for a new piece of memory, and after copying the content, release the original memory space
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,防止野指针
}

placeholder for int | char array

int nums[10];
char str[10];

数组未填充会有占位符
int数组占位符:0 | 随机数字
char数组占位符:'\0'

Structure struct use (struct type + struct pointer)

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

References to Xcode projects

Actual referenced code example (demo.h/demo.c)

#ifdef __cplusplus
extern "C" {
#endif

extern int demo_main(int argc, char * argv[]);

#ifdef __cplusplus
}
#endif
#include "demo.h"

Two, OC

APP Constructor & Destructor

__attribute__((constructor))  Constructor declaration (called before APP enters main) -  self-registration implementation of module startup items for iOS cold start optimization

#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))  Destructor declaration (called before the APP process is killed)

__attribute__((destructor)) 
static void exit_app(void) {
    // 监听APP被杀掉的频率
}

Using 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 get method and property

Determine whether to declare a protocol, and how to implement it

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

Class acquisition methods, properties and protocols

- (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 acquisition method and attribute

- (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 attribute lazy loading

@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 structure

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 analysis - memory address

Memory address: hexadecimal, Mach-O original memory address.
Memory address = Hex (decimal of offset address - offset)

Open Hopper Disassembler , import the Mach-O file, and lock the specific position through the offset .

Using Hopper for assembly-based iOS-Crash stack analysis

iOS system version Frameworks path : ~/Library/Developer/Xcode/iOS DeviceSupport/16.1.2 (20B110) arm64e/Symbols/System/Library/Frameworks/UIKit.framework 

file memory mapping mmap 

mmap - memory mapping, the most commonly used scenario is MMKV (mmap key value) - the application of MMKV in iOS , followed by log printing. Store data through the memory map provided by the system, which can avoid data loss caused by APP being killed | crash.

mmap maps the address information of the file on the disk with the virtual logical address used by the process. The process of establishing the mapping is different from ordinary memory reading: normally, the file is copied to the memory, and mmap only establishes the mapping without loading the file into in memory.

iOS file memory mapping mmap

Spinlocks and mutexes

Performance overhead of various locks

Mutex: The caller judges whether the holder is available, and terminates if it is not available. The mutex will cause the caller to sleep.

  • pthread_mutex
  • NSLock
  • NSRecursiveLock
  • NSConditionLock
  • @synchronized

Spin lock: The caller will keep looping to determine whether the holder has been released. If it has been released to execute the next lock, the spin lock will not cause the caller to sleep, but it consumes more CPU resources (atomic operation).

  • atomic
  • dispatch_semaphore_t
  • OSSpinLock (with priority inversion problem)
// 读写锁-多读单写 (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 requests bypass the global proxy

connectionProxyDictionary is set to an empty dictionary, canceling the global proxy and making the packet capture tool invalid

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.connectionProxyDictionary = @{};

HTTPS certificate configuration and verification

Handle the callback method corresponding to 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);
    }
}

Verify whether the server is trustworthy, add the switch to allow invalid certificates and local certificate verification 

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;

Associate the domain name with the certificate issued by the server to implement the verification logic of the cer certificate or public key

- (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 application

The use of mas_makeConstraints must be after addSubView , otherwise an assertion will be triggered.

mas_equalTo: The input parameter can be an id object or a number type. The input parameter is an id object, which is similar to equalTo, which represents a fixed pixel value for a number type, or this is a relative offset

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

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

equalTo(id): view-reference view, viewAttribute-reference view constraint value, number-fixed pixel/relative offset

offset: offset (CGFloat type)

/* 等价调用示例 */
[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);
}];

Add/update constraints using mas_updateConstraints

// 更新约束
[subView mas_updateConstraints:^(MASConstraintMaker *make) {
    make.bottom.equalTo(view).offset(-15);
}];

[view setNeedsUpdateConstraints];

Add/update/remove constraints using 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 layout update: After the setNeedsUpdateConstraints flag needs to update the layout, you can call updateConstraintsIfNeeded to execute immediately, or you can wait for the mainRunLoop of the main thread to trigger the execution.

- (void)setNeedsUpdateConstraints  标记需要进行重新布局
- (void)updateConstraintsIfNeeded  调用此方法,如果有标记为需要重新布局的约束,则立即进行重新布局,内部会调用updateConstraints方法

- (void)updateConstraints          重写此方法,内部实现自定义布局过程
- (BOOL)needsUpdateConstraints     当前是否需要重新布局,内部会判断当前有没有被标记的约束

ReactiveObjC application

1. RACChannelTo : realize the two-way binding of view-model;

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

2. RACObserve : Subscribe to the change signal of the attribute and realize the one-way binding of 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);

 Listen to Notification notification events

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

3. RACSubject : subscribe to signals, send signals, used to replace or enhance the native delegate and block capabilities;

RACSubject* subject = [RACSubject subject];
    
/// 订阅信号
[subject subscribeNext:^(id x) {
        
}];
    
/// 发送信号
[subject sendNext:nil];

4. RACCommand : Realize data acquisition (create signal, subscribe signal, subscribe execution status, subscribe exception, execute command, send signal, send exception, end signal);

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

Get the bundle object

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

Platform differences of image MD5

Header information is added to iOS image metadata, which makes the metadata MD5 signature inconsistent with other platforms.

3. JS

Constructor

Declare the properties and methods of the constructor

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

Use prototype to implement constructor inheritance

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

Use prototype to implement the extension method of the constructor

MyClass.prototype.callBack = function(result) {
  // callBack方法实现
  console.log(this.myProperty);
  console.log(result);
};

Create an instance of the constructor and execute the method

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

Mount the method on the instance, call the method

instance.handleMessage = function(message) {
  // 处理消息
  console.log(message);
};

instance.handleMessage(JSON.stringify({type: 'notify', payload: {}}));

Guess you like

Origin blog.csdn.net/z119901214/article/details/122344654