美团实习时,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