内联函数、使用类别增加属性、struct类型转换

为View增加平滑的圆角

项目改版,需要在项目中实现整体的一个平滑的圆角的方案,实现过程遇到诸多问题,记录一下,以免日后再次踩坑。

因为是使用category实现的,想通过category给view增加一个可以设置四个角弧度的属性,category里面增加属性的时候,自定义了一个struct类型的属性,

typedef struct Corner {
	CGFloat topLeft;
	CGFloat topRight;
	CGFloat bottomLeft;
	CGFloat bottomRight;
} Corner

其实这里就是自定义了一个结果提,然后对他进行了重命名 叫Corner,也可以改成 AA,BB 一个名字而已;就等价于

struct Corner {
	CGFloat topLeft;
	CGFloat topRight;
	CGFloat bottomLeft;
	CGFloat bottomRight;
}
typedef  struct Corner  Corner

然后用到了一个内联函数,关于内联函数,这里复习一下,内联函数称为在线函数,或者编译时期展开函数,用inline 修饰的函数,普通函数之间的调用,是内存地址之间的调用,在被调用的时候需要CPU执行CALL执行不同,需要完成程序计数器压栈->执行要执行的函数语句->出栈程序计数器,会有一个时间开销,内联函数不需要这个调用过程,内联函数是使用空间换时间的典型案例,在编译的时候,内联函数会直接在需要执行内联函数的地方(普通函数执行CALL的地方),将内联函数汇编片段copy一份并插到此处,代替CALL指令,类似于宏定义,但是宏定义只是简单的字符串替换,是不能做类型检查的,内联函数相对比而言更安全;所以执行速度比一般函数的执行速度提高了这里的效率;但是由于copy了代码片段,需要内存消耗,所以是空间换时间的一种方法;内联函数一般是比较简单的代码片段,不能包括循环、递归(递归函数的内联扩展可能引起部分编译器的无穷编译)等复杂流程,代码最好不超过5行,在YYKit 和 AFNetworking 这些三方框架中,有大量的内联函数的使用;

UIKIT_STATIC_INLINE SquirCleRadius SOCornerRadiusMake(CGFloat topLeft, CGFloat topRight, CGFloat bottomLeft, CGFloat bottomRight) {
    SquirCleRadius radius = {topLeft, topRight, bottomLeft, bottomRight};
    return radius;
}

这里  UIKIT_STATIC_INLINE 是一个宏定义  - >  #define UIKIT_STATIC_INLINE	static inline

在category.h中,代码如下

#import <UIKit/UIKit.h>

typedef struct SquirCleRadius {
    CGFloat topLeft;
    CGFloat topRight;
    CGFloat bottomLeft;
    CGFloat bottomRight;
}SquirCleRadius;

UIKIT_STATIC_INLINE SquirCleRadius SOCornerRadiusMake(CGFloat topLeft, CGFloat topRight, CGFloat bottomLeft, CGFloat bottomRight) {
    SquirCleRadius radius = {topLeft, topRight, bottomLeft, bottomRight};
    return radius;
}

NS_ASSUME_NONNULL_BEGIN

@interface UIView (Squircle)


@property (nonatomic,assign) CGFloat controlRatio;

@property (nonatomic,assign) SquirCleRadius radii;

@end

NS_ASSUME_NONNULL_END

category.m

@implementation UIView (Squircle)

- (void)setRadii:(SquirCleRadius)radii{
    NSValue *radiiValue = [NSValue valueWithBytes:&radii objCType:@encode(struct SquirCleRadius)];
    objc_setAssociatedObject(self, @"radiiKey", radiiValue, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [self updateSquirclePath];
}
- (SquirCleRadius)radii{
    NSValue *value = objc_getAssociatedObject(self, @"radiiKey");
     if(value) {
         SquirCleRadius radii;
         [value getValue:&radii];
         return radii;
     }else {
         return SOCornerRadiusMake(0, 0, 0, 0);
     }
}
- (void)setControlRatio:(CGFloat)controlRatio{
    objc_setAssociatedObject(self, @"controlRatioKey", @(controlRatio), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [self updateSquirclePath];
}
- (CGFloat)controlRatio{
    id controlRatio = objc_getAssociatedObject(self, @"controlRatioKey");
    if (controlRatio == nil || [controlRatio isEqual:[NSNull null]]) {
        return 1;
    }else{
        return [controlRatio floatValue];
    }
}

- (void)updateSquirclePath{
    CAShapeLayer *shapelayer = [CAShapeLayer layer];
    shapelayer.path = [self getSquirCleBezierPath].CGPath;
    shapelayer.fillColor = [UIColor blueColor].CGColor;
    [self.layer addSublayer:shapelayer];
    self.layer.mask = shapelayer;
}
- (UIBezierPath *)getSquirCleBezierPath{
    
    UIBezierPath *path = [UIBezierPath bezierPath]; 
    return path;
}
@end

在.m 里面可以看到,使用关联对象的只是,给category增加属性,最后存储的都是id类型

objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)

objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)

所以这里要把struct 转换为id类型,转换为value类型

NSValue *customValue = [NSValue valueWithBytes:&tmpTest objCType:@encode(struct Test)];

再把get得到的id类型转换为struct类型

NSValue *value = objc_getAssociatedObject(self, @"radiiKey");
if(value) {
     SquirCleRadius radii;
      [value getValue:&radii];
      return radii;
}else {
      return SOCornerRadiusMake(0, 0, 0, 0);
}

让实际存储数据以NSValue的形式存储;
那么又有一个问题,我以为,Struct是用assign来修饰的,那么使用关联对象的增加属性的时候,就自然而言的选择了 OBJC_ASSOCIATION_ASSIGN,使用assign策略的特点是,对象释放以后,不会主动将修饰对象置为nil,这样会有访问僵尸对象导致崩溃的风险,为了解决这个问题,可以创建一个替身对象,使用 OBJC_ASSOCIATION_RETAIN_NONATOMIC 策略来强引用对象,既然关联对象存取都是对象,那么 大多数应该都是用 OBJC_ASSOCIATION_RETAIN_NONATOMIC

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值