知识点

296 阅读3分钟

1. 系统NavigationBar的Title两行显示 & Label的多行富文本操作

- (void)replaceNavigationTitleView
{
    // 初始化一个Label
    UILabel *customTitleView = [[UILabel alloc] init];
    customTitleView.width = 180;
    customTitleView.height = 44;
    customTitleView.numberOfLines = 0;
    
    // 字符串赋值
    NSString *name = @"Jaten";
    NSString *detail = @"这是详细信息";
    NSString *str = [NSString stringWithFormat:@"%@\n%@", name, detail];
    
    // 设置段落风格
    NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
    // 行间距
    [paragraphStyle setLineSpacing:3.f];
    // 此处会替换Label的NSTextAlignmentCenter,所以要在这里设置
    [paragraphStyle setAlignment:NSTextAlignmentCenter];
    
    // 富文本
    NSMutableAttributedString * attrStr = [[NSMutableAttributedString alloc]initWithString:str];
    [attrStr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:15] range:[str rangeOfString:name]];
    [attrStr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:11] range:[str rangeOfString:detail]];
    [attrStr addAttribute:NSForegroundColorAttributeName value:[UIColor grayColor] range:[str rangeOfString:detail]];
    [attrStr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [str length])];
    customTitleView.attributedText = attrStr;

    // 最后设置titleView
    self.navigationItem.titleView = customTitleView;
}

2. atomic 和 non-atomic

两者的相同点:

系统都会自动生成getter/setter方法

两者的区别主要在于:

atomic:系统自动生成的getter/setter方法会进行加锁操作

nonatomic:系统自动生成的getter/setter方法不会进行加锁操作

用代码来表示:

atomic:

// mrc 环境
// implementation
@synthesize icon1 = _item1;

//set
-(void)setItem1:(UIImage *)item1
{
    //同步代码块
    @synchronized (self) {
        
        if(_item1 != item1)
        {
            [_item1 release];
            _item1 = [item1 retain];
        }
    }
}

//get
-(UIImage *)item1
{
    UIImage *image = nil;
    //同步代码块
    @synchronized (self) {
        
        image = [[_item1 retain] autorelease];
    }
    return image;
}

nonatomic:

// mrc 环境
// implementation
@synthesize icon = _item;

//set
-(void)setItem:(UIImage *)item
{
    if(_item != item)
    {
        [_item release];
        _item = [item retain];
    }
}

//get
-(UIImage *)item
{
    return _item;
}

虽然系统会为atomic声明的getter/setter方法加锁,但并不能保证程序的线程安全,原因是:

①调用getter

②调用setter

③调用setter

在setter存时,确实会不受其他getter/setter语句的影响,但是getter取时,由于无法保证线程的进行情况,可能取到的值有三种:未被②③改变的值;被②改变的值;被③改变的值。

结论:

所以atomic也不是线程安全的,我们还需要自己在用到多线程时谨慎处理。而在iOS设备上,nonatomic因为没有那么多复杂的加锁步骤,存取速度会比atomic快,再加上iOS设备的核心CPU数较少,atomic的加锁机制起到的作用很小,所以nonatomic还是相对atomic比较好用和高效的属性。


3. scrollView下拉深度埋点

最近业务中遇到了scrollView下拉深度埋点的需求,要求统计用户在浏览某个collectionView或tableView的时候下拉的深度。

需要埋点的页面要实现scrollView的scrollViewWillEndDragging:方法,在下拉列表并松开时,通过targetContentOffset来计算整个页面的下拉深度。

/*
 下拉深度埋点
 @param pageName             页面名称
 @param scrollView           目标scrollView
 @param targetContentOffsetY 即将滑动偏移量
 @param paramDict            其他参数
 */
+ (void)sScanDeepStatisticsPageName:(NSString *)pageName scrollView:(UIScrollView *)scrollView targetContentOffsetY:(CGFloat)targetContentOffsetY otherParameters:(NSDictionary *)paramDict
{
    // 取出此scrollView对应的最大浏览深度
    NSInteger longestDeep = [[SPointManager sharedManager] getLongestScreenDeepWithPageName:pageName scrollViewTag:scrollView.tag];
    
    // 判断是否比之前更深
    NSInteger currentDeep = (scrollView.y + scrollView.height + contentOffsetY) / 1.0;
    if (currentDeep == 0 || currentDeep <= longestDeep) {
        return;
    }
    
    // 存入最大浏览深度
    [[SPointManager sharedManager] saveLongestScreenDeepWithLongestScreenDeep:currentDeep PageName:pageName scrollViewTag:scrollView.tag];
    
    // 通过接口发送至后台进行统计
}

以下是sPointManager类的代码,用于所有scrollView当前对应深度的存取

// .h文件

@interface SPointManager : NSObject

+ (SPointManager *)sharedManager;

// tag是为了区分某个页面具有多个scrollView
- (NSInteger)getLongestScreenDeepWithPageName:(NSString *)pageName scrollViewTag:(NSInteger)tag;
- (void)saveLongestScreenDeepWithLongestScreenDeep:(NSInteger)longestScreenDeep PageName:(NSString *)pageName scrollViewTag:(NSInteger)tag;

@end

// .m文件

@interface SPointManager ()

@property (nonatomic, strong) NSMutableDictionary *dict;

@end

@implementation SPointManager

+ (SPointManager *)sharedManager
{
    static SPointManager *sPointManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sPointManager = [[self alloc] init];
    });
    return sPointManager;
}

- (NSInteger)getLongestScreenDeepWithPageName:(NSString *)pageName scrollViewTag:(NSInteger)tag
{
    NSString *key = [NSString stringWithFormat:@"%@%ld", pageName, tag];
    return [[self.dict objectForKey:key] integerValue];
}

- (void)saveLongestScreenDeepWithLongestScreenDeep:(NSInteger)longestScreenDeep PageName:(NSString *)pageName scrollViewTag:(NSInteger)tag
{
    NSString *key = [NSString stringWithFormat:@"%@%ld", pageName, tag];
    [self.dict setObject:[NSString stringWithFormat:@"%ld", longestScreenDeep] forKey:key];
}

- (NSMutableDictionary *)dict
{
    if (!_dict) {
        _dict = [NSMutableDictionary dictionary];
    }
    return _dict;
}

@end