IOS成长记录(三)

来源:互联网 发布:pdg for mac 编辑:程序博客网 时间:2024/05/17 01:55

一.UITableViewRowAnimation


(void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation NS_AVAILABLE_IOS(3_0);

这个方法的第二个参数是设置cell的动画的,很坑爹,一开始设置成UITableViewRowAnimationAutomatic,每次刷新cell的时候都会闪烁一下,后来改成UITableViewRowAnimationNone就没了,有时间一定好好研究第二个参数的结构体。

二.UItableViewCell重用

今天下午调试了一下UITableViewCell的重用,当我动态的往一个cell里面添加一个控件,或者改变他的颜色等属性的时候,在重用的时候,会消失,但是高度却会保留

三.开发顺序

  1. 搭建界面
  2. 展示数据,按照返回的数据设计模型,并且把字典转换成模型(MJExtension)
  3. 处理对应的业务逻辑

对于微博项目,培训课程里面的顺序把cell的内部开发放到了接受网络数据之后,只是简单的显示一下数据,处理网络图片采用了SDWebImage来处理

四.小知识点合集

1.控制器的View是懒加载的,只有在要使用view的时候才会调用viewDidLoad等方法,并不会在控制器创建的时候调用,但是对于UITableViewController不是这样的

五.appearance方法

在开发中经常会有些类有一个方法叫做appearance,通过UIAppearance设置一些UI的全局效果,这样就可以很方便的实现UI的自定义效果又能最简单的实现统一界面风格。
这个协议我们比较常用的又两个方法:
1. appearence返回的是要修改全局的属性的类的实例:

  UINavigationBar * appearance = [UINavigationBar appearance];  UIImage *navBackgroundImg =[UIImage imageNamed:@"navBg.png”];  [appearance setBackgroundImage:navBackgroundImg forBarMetrics:UIBarMetricsDefault];
  1. (id)appearanceWhenContainedIn:(Class <>)ContainerClass,…
    这个方法可设置某个类的改变:例如:设置UIBarButtonItem 在UINavigationBar、UIPopoverController、UITabbar中的效果。就可以这样写:
[[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], [UIPopoverController class],[UITabbar class] nil] setTintColor:myPopoverNavBarColor];
//修改导航条上的UIBarButtonItem    UIBarButtonItem *appearance = [UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil];    //设置导航栏的字体包括backBarButton和leftBarButton,rightBarButton的字体    NSDictionary *textAttributes = @{UITextAttributeFont: [UIFont systemFontOfSize:18],                                     UITextAttributeTextColor: [UIColor blueColor],                                     UITextAttributeTextShadowColor: [UIColor whiteColor],                                     UITextAttributeTextShadowOffset: [NSValue valueWithCGSize:CGSizeMake(1, 1)]};    [appearance setTitleTextAttributes:textAttributes forState:1];//forState为0时为下正常状态,为1时为点击状态。

六.load和initialize方法

在于iOS会在运行期提前并且自动调用这两个方法,而且很多对于类方法的规则(比如继承,类别(Category))都有不同的处理,而因为这两个方法是在程序运行一开始就被调用的方法,我们可以利用他们在类被使用前,做一些预处理工作。
load是只要类所在文件被引用就会被调用,而initialize是在类或者其子类的第一个方法被调用前调用。所以如果类没有被引用进项目(只要这个类的文件在项目中,无论这个类是否被引用,都会被调用),就不会有load调用;但即使类文件被引用进来,但是没有使用,那么initialize也不会被调用。
它们的相同点在于:方法只会被调用一次。父类(Superclass)的方法优先于子类(Subclass)的方法,类中的方法优先于类别(Category)中的方法。

七.快速初始化方法

在一些自定义的父类中,我们可能为了方便来为它写了一个快速的初始化方法,如下:

+ (instancetype) quickInitial{    return [[self alloc] init];}

在return中我们使用的是self而不是该父类的名字,这样如果有子类继承了这个方法,如果用父类的名字初始化,那就会返回的是父类的实例,导致错误

八.KVC和KVO小分析

KVC

其实对于KVC采用的就是oc的运行时机制,在运行的时候动态的搜索类的方法和变量,我们熟知的KVO和CoreData等都是以KVC为基础的,后面我们会具体分析一下。我们在设计模型的时候如果把模型的里面的值的名字设计成下面所说的那些类型的话,setValuesForKeysWithDictionary是可以自动识别并且赋值的

(void)setValue:(id)value forKey:(NSString *)key

对于上面这个方法,kvc的主要原理还是查找key的set方法,如果找到就使用set方法给变量赋值,但是有三种特殊的情况:
1. value是nil的时候,会调用setNilValueForKey:这个方法来赋值
2. 如果没有找到set方法,且接受者的类的+accessInstanceVariablesDirectly方法返回YES,那么就查找这个接受者的与key相匹配的实例变量(匹配模式为_,_is,,is):比如:key为age,只要属性存在_age,_isAge,age,isAge(对于这四种值的匹配是按照先后顺序的)中的其中一个就认为匹配上了,如果找到这样的一个实例变量,并且的类型是一个对象指针类型,首先released对象上的旧值,然后把传入的新值retain后的传入的值赋值该成员变量
3. 如果访问器方法和实例变量都没有找到,执行setValue:forUndefinedKey:方法,该方法的默认实现是产生一个 NSUndefinedKeyException 类型的异常,但是这个方法我们是可以重写的

KVO

其实对于KVO最核心的还是重写key的setter方法,来做到监听的目的
(void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
当我们使用KVO的时候,它大体会干下面几件事:
1. 跟KVC一样,先查找一下这个这个被观察的key是否有setter方法,没有的话,那就直接没法监听了
2. 如果有setter方法,它会创建一个继承于这个对象的类的子类,像下面这样:

Class clazz = object_getClass(self);    NSString *className = NSStringFromClass(clazz);    if (![className hasPrefix:kPGKVOClassPrefix]) {        clazz = [self makeNewClassFromOriginClass:className];        object_setClass(self, clazz);    }
  1. 创建好子类以后,它会往子类中写进一个setter方法,也就是上面那个key的setter方法:

    const char *types = method_getTypeEncoding(setterMethod);class_addMethod(clazz, setterSel, (IMP)kvo_setter, types);

    在class_addMethod中,我们定义了setter方法的名字,也就是第二个参数setterSel,和这个方法的实现,也就是第三个参数kvo_setter。

  2. 把上面的观察者,也就是observer,关联到调用这个addObserver…的类的观察者列表里面,如下:
   //关联观察者    NSMutableArray *observers = objc_getAssociatedObject(self, (__bridge const void *)(kPGKVOAssociatedObservers));    if (!observers) {        observers = [NSMutableArray array];        objc_setAssociatedObject(self, (__bridge const void *)(kPGKVOAssociatedObservers), observers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);    }    [observers addObject:info];

当有消息产生的时候,就会调用上面我们设置的那个kvo_setter方法,它的大概实现如下:

static void kvo_setter(id self, SEL _cmd, id newValue){    NSString *setterName = NSStringFromSelector(_cmd);    NSString *getterName = getterForSetter(setterName);    struct objc_super superClass = {        .receiver = self,        .super_class = class_getSuperclass(object_getClass(self))    };    //将消息转发给父类的setter方法    void (*objc_msgSendSuperCasted)(void *, SEL, id) = (void *)objc_msgSendSuper;    objc_msgSendSuperCasted(&superClass,_cmd,newValue);    NSMutableArray *observers = objc_getAssociatedObject(self, (__bridge const void *)(kPGKVOAssociatedObservers));    for (PGObservationInfo *each in observers) {        if ([each.key isEqualToString:getterName]) {            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{                [each.observer performSelector:each.selector withObject:nil];            });        }    }}

从上面我们可以看出,当我们已修改这个观察的对象的值的时候,就会调用kvo_setter这个方法,它先是把消息转发给它的父类的setter方法,也就是我们要观察的那个对象的原来的setter方法,并且在下面从观察者队列里面取出观察者,然后调用回调的方法。代码链接我贴在下面了,已经加好注释了。
链接: http://pan.baidu.com/s/1dDAX2tN 密码: k8ct

九.关联模式

oc的扩展机制有两个特性,category即类型,可以通过它来扩展方法;associative,可以通过它来扩展属性;

使用关联,我们可以不用修改类的定义而为其对象增加存储空间。这在我们无法访问到类的源码的时候或者是考虑到二进制兼容性的时候是非常有用。

关联其实就是把两个对象关联起来,也就是说,把一个对象作为了另一个对象的一部分,关联是基于关键字的,因此,我们可以为任何对象增加任意多的关联,每个都使用不同的关键字即可。关联是可以保证被关联的对象在关联对象的整个生命周期都是可用的(在垃圾自动回收环境下也不会导致资源不可回收)。
在关联这里,涉及到三个函数:
- objc_setAssociatedObject ,该函数有四个参数,依次是:源对象,关键字,关联的对象和一个关联策略(这里这么说其实比较难理解),源对象就是将要扩展的对象,关键字就是要扩展的对象的属性,关联的对象就是这个属性的值;关键字是一个void类型的指针。每一个关联的关键字必须是唯一的。通常都是会采用静态变量来作为关键字。关联策略表明了相关的对象是通过赋值,保留引用还是复制的方式进行关联的;还有这种关联是原子的还是非原子的。这里的关联策略和声明属性时的很类似。这种关联策略是通过使用预先定义好的常量来表示的。下面引用一段别人的代码:

    static char overviewKey;    NSArray * array =[[NSArray alloc] initWidthObjects:@"One", @"Two",      @"Three", nil];    //为了演示的目的,这里使用initWithFormat:来确保字符串可以被销毁    NSString * overview = [[NSString alloc] initWithFormat:@"@",@"First     three numbers"];    objc_setAssociatedObject(array, &overviewKey, overview,                 OBJC_ASSOCIATION_RETAIN);    [overview release];    //(1) overview仍然是可用的    [array release];    //(2)overview 不可用
  • objc_getAssociatedObject ,该函数用来获取关联的对象:
    NSString * associatedObject = (NSString *)objc_getAssociatedObject(array, &oveviewKey);

  • objc_removeAssociatedObjects,这个函数会断开所有的关联,所以不建议使用,我们如果要断开关联,可以使用objc_setAssociatedObject,如下:
    objc_setAssociatedObject(array, &overviewKey, nil, OBJC_ASSOCIATION_ASSIGN);

下面贴一个完整的实现程序:

#import <Foundation/Foundation.h>#import <objc/runtime.h>int main(int argc, const char* argv[]){    NSAutoreleasePool * pool = [[NSAutoreleasePool] alloc init];    static char overviewKey;    NSArray *array =[[NSArray alloc] initWidthObjects:@"One", @"Two", @"Three", nil];    //为了演示的目的,这里使用initWithFormat:来确保字符串可以被销毁    NSString * overview = [[NSString alloc] initWithFormat:@"@",@"First three numbers"];    objc_setAssociatedObject(array, &overviewKey, overview, OBJC_ASSOCIATION_RETAIN);    [overview release];    NSString *associatedObject = (NSString *)objc_getAssociatedObject(arrray, &overviewKey);    NSLog(@"associatedObject:%@", associatedObject);    objc_setAssociatedObject(array, &overviewKey, nil, OBJC_ASSOCIATION_ASSIGN);    [array release];    [pool drain];    return 0;}

其实关联就是用来描述两个对象之间的某种关系的,就像key-value一样,如果我要扩展一个不开源的类的属性的时候,我觉得使用关联是很方便的,但是其余的场合,我就不清楚了,如果有知道的前辈,望在评论中指出。

2 0
原创粉丝点击