在美团实习的iOS笔记

美团实习时,OC要从头学起,记一些笔记,方便日后查阅

cell模块的编写

- (void) addSubviews;//方法中添加subview
- (void) updateSubviews;//给subview中的组件赋值
- (void) layoutSubviews;//定义组件位置大小

为什么要使用模块化开发,有利于团队协作,每个人的任务是几个cell的编写,界面渲染时从服务器获得相应的注册的模块

whiteboard

同一个页面下多个模块共享的变量,通过key-value获得

property

属性其实说直白点就是 ivar + setter + getter(实例变量+存取方法)

@synthesize 该关键字指定了属性的实例变量名称,并且根据存储语义(readwrite、readonly)系统自动合成setter和getter方法

@dynamic 告诉编译器不要生成setter,getter方法,由自己实现

atomic原子性读写都是同步,影响效率,nonatomic

readonly,readwrite

assign 基础数据的简单赋值操作 id(id 是一个指向任何一个继承了Object类的对象,是指针) delegate NSInteger CGFloat 为什么可以用assign修饰基本数据类型?因为基础数据类型一般分配在栈上,栈的内存会由系统自己自动处理,不会造成野指针。

strong,weak,unsafe_unretained

copy: setter的时候传进来的对象不会引用它,而是拷贝一份

对象内部尽量直接访问实例变量

惰性加载: 变量会在第一次getter时初始化

category

对类的功能进行扩展,局限性只能扩展方法,不能增加成员变量,出现同名方法优先调用分类中的方法

利用运行时来为分类添加属性 objc_setAssociatedObject

extension是匿名的category,可以添加属性或变量,声明的方法需要在implementation中实现

SEL

Person *p = [Person new];
SEL s = @selector(test);//p中必须有方法叫test,否则报错
[p performSelector:s];

2018.8.8碰到的问题

cell的高度突然多出一大块,原因是因为cell的宽度与当前屏幕不同,原因是cell还没有放入tableview,是初始宽度,因此需要重新设定,不然高度的计算会有问题

sizeToFit 当label有多行时,需要先设定宽度,在sizeToFit 当label只有一行时,如果需要指定宽度则需要先sizeToFit

reduce方法:

headerInfo.desc = [[tableInfo.items cipf_map:^NSString *(NVModelBaseDealStructItem *item, NSUInteger index) {
                        return item.value.length > 0 ? item.value : @"";
                  }] cipf_reduce:^NSString *(NSString *result, NSString *value) {
                        return [result stringByAppendingString:[result.length > 0 ?
                        @"\n":@"" stringByAppendingString:value]];
                  }];
                  //这个方法是把value先提取出来成nsarray在将他开头都加上\n

OC的动态特性

(1)动态类型:体现在id修饰的对象在编译时不会确定类型,而是在运行时才会根据指向的对象来确定

    // 1.id任意类型,编译器就不会把testObject在当成NSString对象了
    id testObject = [[NSData alloc] init];
    // 2.调用NSData的方法编译通过
    [testObject base64EncodedDataWithOptions:NSDataBase64Encoding64CharacterLineLength];
    // 3.调用NSString的方法编译也通过
    [testObject stringByAppendingString:@"string"];

运行时确定testObject为NSData类型,调用NSString的方法会报错

(2)动态绑定:体现在OC将要执行的方法的确定推迟到运行时,可以动态添加方法 (3)动态加载:体现在代码和资源的懒加载模式

当你向一个 Objective-C 的对象发送消息的时候,runtime 沿着对象的 isa 指针找到了这个对象的 Class结构体。 Class 结构体中包含了一个这个类的方法列表和一个 指向父类的指针,用于查找继承的方法。

block生命周期

声明时在栈上,定义后就拷贝放入堆中

//Swift4.0 版本 输出是 10 20
var num = 10
let myBlock = {
    print(num)
}
myBlock()
num = 20
myBlock()

//let myBlock = { [const = num] in
    print(const)
}
//这种写法,捕获的是num的值而不是引用

//OC版本 输出是 10 10
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        int i = 10;
        void (^myBlock)(void) = ^{
            NSLog(@"%d",i);
        };
        myBlock();
        i = 20;
        myBlock();
    }
    return 0;
}

__block int i = 10;//这种情况下输出为 10 20

如何理解自动变量值的截获:block表达式截获的是声明前的变量值 使用clang -rewrite-objc 编译源代码生成的C++代码后,__block会生成一个__Block_byref_i的结构体,block结构体中持有的不再是i而是这个结构体 swift有一些奇怪,他捕获的永远是变量的引用

Swizzle

+ (void)load
{
    Method method1 = class_getInstanceMethod([self class], NSSelectorFromString(@"dealloc"));
    Method method2 = class_getInstanceMethod([self class], @selector(deallocSwizzle));
    method_exchangeImplementations(method1, method2);
}

- (void)deallocSwizzle
{
    NSLog(@"%@被销毁了", self);

    [self deallocSwizzle];
}

在调用当前类的dealloc方法时,会去调用deallocSwizzle方法,输出一句话,在调用dealloc方法

Swizzle使用指南 最好写在对象的load方法中

Load vs Initialize

Load方法是在类加载是调用的,先调用父类的方法,最后调用子类的。分类的load晚于主类的load。子类父类分类中的load都是各自实现的

Initialize是懒加载的,是在类收到第一条消息时调用的。如果子类没有实现initialize那么会调用父类的实现.initialize本质是消息发送,与普通方法一致

objc_class、objc_object、Class

id 是指向一个objc_object的指针。objc_object有一个指针指向它的Class

Class 本身指向的也是一个C的 struct objc_class

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    // 省略方法
}

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

浅浅的理解SideTable

SideTables是一个全局的存储对象的引用计数的HashMap,内存地址为key,value为SideTable结构体。SideTables一共有64个单元格

SideTable结构 spinlock, RefcountMap, weak_table

RefcountMap可以理解为一个32或者64位的数,从低到高第一位标识是否是weak,第二位标识是否正在deallocing,最高位标识引用计数是否达到上限 其余位置标识真正的引用计数

Tagged Pointer

原本64位的cpu存储一个NSNumber需要4字节指针与4字节值,使用Tagged Pointer后将值直接存储在指针中,并加上标识,这样可以节约一半的空间

所有对象都有 isa 指针,而Tagged Pointer其实是没有的,因为它不是真正的对象

马总亮哥的几个面试小问题

1.weak和assign的区别:assign修饰的对象被释放后,指针的地址还是存在的。weak修饰的对象在释放之后,指针地址会被置为nil。 2.atomic修饰NSMutableArray时addObject是线程安全的吗?不是,因为atomic修饰后仅对get,set方法有原子性操作。 3.深拷贝与浅拷贝的区别,OC如何实现深拷贝 4.runtime的消息发送机制,三步。 5.category和extension的区别 6.catogory有同名方法时优先调用category,为什么?

runtime实现weak

id obj1;
objc_initWeak(&obj1,obj);
objc_destroyWeak(&obj1);

//等价
id obj1;
obj1 = 0;
objc_storeWeak(&obj1, obj);
/* ... obj的引用计数变为0,被置nil ... */
objc_storeWeak(&obj1, 0);

weak修饰的指针默认是nil