PART 5

便利构造函数

既然要说便利构造函数(Convenience Initializer)就不得不先提起指定构造函数(Designated Initializer)。前者是Swift特有的概念,后者是OCSwift共有的初始化对象的方式,形如:

//OC
- (instancetype)initWithXXXX {
    if (self = [super init]) {

    }

    return self;
}
//Swift
init(....) {
    //属性的初始化

    super.init()
}

需要明确注意的是:OC中,初始化的对象需要显示返回,并且当前对象属性的初始化需要在父类初始化后进行。而在Swift中,以上过程恰恰相反。(指定构造器中)

指定构造器正常情况下都会创建对象,而便利构造器会根据条件判断创建对象与否,对于不合理调用构造函数就可以做到减小内存开销问题。便利构造器本身不负责对象的创建,这就要求其内部必须调用当前类的指定构造器来实例化对象。所以如果要在便利构造函数中使用当前对象的属性,就需要在调用指定构造函数之后。此外,还可用extension给类扩展一个便利构造函数简化对象的创建。

NSString属性为什么用copy关键字

要说明这个观点的必要性就先说明为什么不用strongretain。它们两者都是增加原对象的引用计数,来达到持有该对象的目的。在类的关系能正确对应的情况下,即NSString对象 -> NSString类型的属性。对,当遇上NSMutableString时,情况就变糟糕了。此时使用strong或者retain,当原对象改变了之后,属性引用的对象也会随之改变。

copy是不可变的复制。针对于不可变对象,此时的复制只是浅复制。如NSArray、NSDictionary、NSString、NSSet。此时针对于被复制的对象本身,使用strongretain也不会有什么问题。当被复制的对象是可变时,复制后虽然会产生新的对象,但集合内部的元素只是增加了它的引用计数而已,元素本身没有被复制。使用LLDBexpression判断元素相等,你会得到肯定的结果。就效率和安全性来说,使用copy是毫无疑问的。

UICollectionView自定义约束实现

系统提供了一个约束来为UICollectionView实现流式布局——UICollectionViewFlowLayout,也就是网格布局。这能满足一些场景的需求,如果你想要更灵活的玩耍UICollectionView,你就需要自定义约束类来添加各种有意思的约束,这个类是UICollectionViewLayout,你需要实现如下几个方法:

// 开始时调用的方法,每次只执行一次,准备工作,不执行任何布局。一般在该方法中设定一些必要的layout的结构和初始需要的参数等。以下用官方提供的圆形布局作为讲解参考
// 调用顺序:1
- (void)prepareLayout {
    // 保证约束实例能正常工作
    [super prepareLayout];

    // UICollectionViewLayout持有使用它布局的UICollectionView实例
    CGSize size = self.collectionView.frame.size;

    // 获得当前单元格的数量
    _cellCount = [self.collectionView numberOfItemsInSection:0];

    // 圆环的圆心
    _center = CGPointMake(size.width / 2.0, size.height / 2.0);

    // 以UICollectionView宽高中较小值的2.5分之一作为圆环的半径
    _radius = MIN(size.width, size.height) / 2.5;
}

// 确定collection应该占据的尺寸。注意这里的尺寸不是指可视部分的尺寸,而应该是所有内容所占的尺寸。collectionView是scrollView的子类,因此需要这个尺寸来配置滚动行为。在这个例子中,不需要滚动,就直接返回视图的尺寸。
// 2
- (CGSize)collectionViewContentSize {
   // NSLog(@"%f", self.collectionView.frame.size.height);
    return self.collectionView.frame.size;
}

// 指定CGRect范围内的单元格大小和位置
// 3
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSMutableArray *attributes = [NSMutableArray array];
    // rect表示的是UICollectionView所有内容所占的矩形区域的尺寸,即在CollectionViewContentSize设定的尺寸
    for (NSInteger i = 0; i < _cellCount; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        [attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
    }

    return attributes;
}

// 返回UICollectionViewLayoutAttributes控制指定的单元格大小和位置。
// 4
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    // cosf, sinf分别表示余弦函数,正弦函数
    attributes.size = CGSizeMake(ITEM_SIZE, ITEM_SIZE);
    attributes.center = CGPointMake(_center.x + _radius * cosf(2 * M_PI * indexPath.item / _cellCount), _center.y + _radius * sinf(2 * M_PI *indexPath.item / _cellCount));

    return attributes;
}

// 当单元格动态开始显示出来时自动调用该方法(实例化对单元格添加的约束)
- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath {

    // 设置item最初的约束,系统会以动画的形式让item完成最终的约束
    UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
    attributes.alpha = 0.0;
    attributes.center = CGPointMake(_center.x, _center.y);

    return attributes;

}

// 当单元格消失的时候自动调用
- (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath {

    // 设置item消失后的约束,与开始相反,系统以动画形式让item从现在的约束过渡到消失后指定的约束
    UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
    attributes.alpha = 0.0;
    attributes.center = CGPointMake(_center.x, _center.y);
    attributes.transform3D = CATransform3DMakeScale(0.1, 0.1, 1.0);

    return attributes;
}

哈希表

关于哈希表(hash table)的概述或者定义这里就不阐述了,因为这样的文章比比皆是,这里主要记录一些哈希表的特性以及在iOS中如何让自定义对象可哈希化。

特性

众所周知的是objc中的字典的底层实现就是哈希表,而它在数据结构上的表现与数组是类似的,即内存空间都是连续的,它的每一元素被称为箱(bin),箱中装的就是键值对。

因此它也具有快速查找的能力,在理想的哈希函数中,无论数据体量有多大,它的一次查询操作的时间复杂度均是O(1)。它实际上会将字典中使用键(key)来找值转换成在数组中使用下标来查找值,所以这里就涉及到如果将键映射成索引下标。

其实,这个过程在理解上很简单:根据指定的规则,以给定的键来计算哈希值(hash value)h,这会是一个数量类型的值。它在哈希表中的位置会通过下面这个等式算出:

// n为哈希表中元素的个数
location = h % n

这个location就相当于数组中元素的下标。

因此就不难得出,发生键值对冲突的原因:相同的键被插入到同一哈希表中。在objc中使用字符串作为键时,发生这种情况的话,后者的插入将会无效,即结果只保留前者。一种名叫拉链法的解决方案是将属于同一箱子的键值对依次排列形成链表。

在正常情况下一个箱子中就只会有一个键值对,即负载因子(load factor:等于键值对总数除于箱子数)为1。负载因子越大表示哈希表越满,越容易发生冲突,效率就越低。一般程序语言中会设置一个阈值,当负载因子大于这个值时,哈希表会自动扩容。此时所有键值对的存放位置都可能发生改变,这个过程叫做重哈希(rehash)。此时由于需要分配新的内存空间,并将之前的键值对赋值到新的内存中,性能会明显下降。因此指定容量创建字典是个很好的决策。

自定义对象哈希化

覆写类的这两个方法:

- (BOOL)isEqual:(id)object {
    //自定义键的判等条件
}

- (NSUInteger)hash {
    //尽可能保证哈希值的唯一
}

一个判等的参考示例是这样的:

- (BOOL)isEqual:(id)object {
    if (!object) {
        return NO;
    }

    if (self == object) {
        return YES;
    }

    if (![object isKindOfClass:[XXX class]]) {
        return NO;
    }

    return [self isEqualToXXX:(XXX *)object];
}

- (BOOL)isEqualToXXX:(XXX *)object {
    //判等条件
}

这样做了之后,你的自定义对象就可以作为字典的键了。

(于2017-8-8记)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值