iOS 使用 lldb 断点加载调试工具如 Reveal-Lookin-Woodpecker
整体的 lldb 加载命令如下, 放在 ~/.lldbinit 文件内 ### Reveal LLDB commands support - DO NOT MODIFY command
这是iOS 每日一题的第二篇了, 坚持下来不容易, 刚开始想每个问题都整理很多, 细细地写下了, 后面发现很多时候,因为工作时间问题,并不能如愿, 而且目前还负责一部分服务器的开发, 又忙了起来. 而最重要的是要坚持做下去这件事, 不在于问题的复杂度. 所以加油!
cancelPreviousPerformRequestsWithTarget
是什么 ? 用在哪些场景 ? 如何做到的 ?查看苹果文档可以看到
/**************** Delayed perform ******************/
@interface NSObject (NSDelayedPerforming)
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes;
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(nullable id)anArgument;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;
@end
@interface NSRunLoop (NSOrderedPerform)
- (void)performSelector:(SEL)aSelector target:(id)target argument:(nullable id)arg order:(NSUInteger)order modes:(NSArray<NSRunLoopMode> *)modes;
- (void)cancelPerformSelector:(SEL)aSelector target:(id)target argument:(nullable id)arg;
- (void)cancelPerformSelectorsWithTarget:(id)target;
@end
// <Foundation/NSThread.h>
@interface NSObject (NSThreadPerformAdditions)
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
// equivalent to the first method with kCFRunLoopCommonModes
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array NS_AVAILABLE(10_5, 2_0);
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
// equivalent to the first method with kCFRunLoopCommonModes
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg NS_AVAILABLE(10_5, 2_0);
@end
此类方法一共有两类:
NSObject 的分类 NSDelayedPerforming
延迟执行的方法
NSRunLoop 的分类 NSOrderedPerform
顺序执行的方法
每一类都有自己的 perform..
和 自己的 cancel...
补充: NSObject 的另一个分类 NSThreadPerformAdditions
, 可以通过指定线程去执行延迟操作
All perform requests are canceled that have the same target as aTarget, argument as anArgument, and selector as aSelector. This method removes perform requests only in the current run loop, not all run loops.
总结:
aTarget
, 务必保证 performSelector...
系列方法的参数一致aSelector
, 务必保证 performSelector...
系列方法的参数一致run loop
也就是说必须在当前的线程才可以, 即便同一个 target
, 同一个 SEL
,只要是不再同一个线程, 也不会起作用.isEqual
来判断 同一性 !The argument for requests previously registered with the performSelector:withObject:afterDelay: instance method. Argument equality is determined using isEqual:, so the value need not be the same object that was passed originally. Pass nil to match a request for nil that was originally passed as the argument.
使用场景:
方法执行
- (void)performSelector:(SEL)aSelector
target:(id)target
argument:(id)arg
order:(NSUInteger)order
modes:(NSArray<NSRunLoopMode> *)modes;
This method sets up a timer to perform the aSelector message on the receiver at the start of the next run loop iteration. The timer is configured to run in the modes specified by the modes parameter. When the timer fires, the thread attempts to dequeue the message from the run loop and perform the selector. It succeeds if the run loop is running and in one of the specified modes; otherwise, the timer waits until the run loop is in one of those modes.
This method returns before the aSelector message is sent. The receiver retains the target and anArgument objects until the timer for the selector fires, and then releases them as part of its cleanup.
Use this method if you want multiple messages to be sent after the current event has been processed and you want to make sure these messages are sent in a certain order.
modes
确定target
和 参数对象, 一直到 这个定时器执行这个方法使用场景:
方法取消
- (void)cancelPerformSelector:(SEL)aSelector target:(id)target argument:(nullable id)arg
This method cancels the previously scheduled messages associated with the target, ignoring the selector and argument of the scheduled operation. This is in contrast to cancelPerformSelector:target:argument:, which requires you to match the selector and argument as well as the target. This method removes the perform requests for the object from all modes of the run loop.
它会取消那些仍然 没有完成 的 任务, 无关任务所在 runloop
运行模式.
注意要取消的行为, 相同的参数, 相同的 target, 相同的方法
todo:// 示例代码
by l.j. twan 2018/11/19
by l.j. twan) 2018/11/20
Runloop
相关的类
既然是保活, 就要有启动一说, 而官方文档中启动方式有
@interface NSRunLoop (NSRunLoopConveniences)
- (void)run;
- (void)runUntilDate:(NSDate *)limitDate;
- (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate;
@end
- (void)run;
内部循环调用 - (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate;
, 一直到 runloop 终结.- (void)runUntilDate:(NSDate *)limitDate;
//内部循环调用 - (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate;
一直到设置的超时- (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate;
// 执行一次, 任务完成退出,或者 执行过程中超时退出.
- (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate;
是其他两种方式的基础操作
NSRunLoopMode
对于这个, 需要了解的是, 子线程即便开启了 run
, 但是如果没有 mode
, 仍然会立即退出, 如果想真正的执行任务, 我们就要指定一个或多个 NSRunLoopMode
才可以.
typedef NSString * NSRunLoopMode NS_EXTENSIBLE_STRING_ENUM;
@property (nullable, readonly, copy) NSRunLoopMode currentMode;
iOS使用的 NSRunLoopMode
总共有 3种.
NSRunLoopCommonModes
Objects added to a run loop using this value as the mode are monitored by all run loop modes that have been declared as a member of the set of “common" modes; see the description of CFRunLoopAddCommonMode for details.
NSDefaultRunLoopMode
The mode to deal with input sources other than NSConnection objects.
UITrackingRunLoopMode
The mode set while tracking in controls takes place. You can use this mode to add timers that fire during tracking.
timers
的问题NSEventTrackingRunLoopMode
macOS
A run loop should be set to this mode when tracking events modally, such as a mouse-dragging loop.
NSModalPanelRunLoopMode
macOS
- A run loop should be set to this mode when waiting for input from a modal panel, such as NSSavePanel or NSOpenPanel
而每种 NSRunLoopMode
运行模式都需要具体的 RunLoopModeItem
, 一共有3类
CFRunLoopSourceRef
: 输入源, 也就是事件产出的地方; 如屏幕触碰, 锁屏等CFRunLoopObserverRef
: 观察源, AutoReleasePool
涉及到的 Runloop
生命状态检测来 管理自动释放池的创建与销毁typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即将进入Loop
kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理 Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7), // 即将退出Loop
};
CFRunLoopTimerRef
: 定时源 , 如平时使用的 NSTimer
执行 : 创建具体的 RunLoopModeItem
并 添加到 指定的 NSRunLoopMode
中.
官方相关 API
/// Schedules the execution of a block on the target run loop in given modes.
/// - parameter: modes An array of input modes for which the block may be executed.
/// - parameter: block The block to execute
- (void)performInModes:(NSArray<NSRunLoopMode> *)modes block:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
/// Schedules the execution of a block on the target run loop.
/// - parameter: block The block to execute
- (void)performBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
上面的是 ios(10.0)
之后的, 不是用来 runloop
启动的, 而是用来执行某个操作, 使用 Block
的方式紧耦合.
如果想退出, 一定不要使用
- (void)run;
去开启Runloop
- (void)runUntilDate:(NSDate *)limitDate;
与 - (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate;
主要是依赖一个 超时时间
去处理CFRunLoopStop()
主动退出 当前的 - (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate;
示例代码摘自 深入研究 Runloop 与线程保活
CFRunLoopStop(CFRunLoopGetCurrent());
NSThread *thread = [NSThread currentThread];
[thread cancel];
by l.j. twan) 2018/11/21
以前遇到一个微信分享的问题, 总是发现有些图片分享, 微信无法正常调用, 检查发现是,项目中的压缩算法得出的图片超过了微信的限制 , 贴一下代码如下
/**
压缩到指定千字节(kb)
*/
+ (NSData *)compressImage:(UIImage *)sourceImage targetKB:(NSInteger )numOfKB {
if (!sourceImage) return nil;
CGFloat compressionQuality = 0.9f;
CGFloat compressionCount = 0;
NSData *imageData = UIImageJPEGRepresentation(sourceImage,compressionQuality);
while (imageData.length >= 1000 * numOfKB && compressionCount < 15) { //15是最大压缩次数.mac中文件大小1000进制
compressionQuality = compressionQuality * 0.9;
compressionCount ++;
imageData = UIImageJPEGRepresentation(sourceImage, compressionQuality);
}
return imageData;
}
把 compressionCount
增大, 效果也不会理想, 很简单的功能, 但是效率太低, 如果采用此种方式, 不进行 compressionCount
的限制, 大抵会严重损耗手机性能.
正确的二分法压缩方式 ✅
摘录代码
NS_INLINE CGFloat clampCompressionFactor(CGFloat factor)
{
return factor <= 1e-10 ? 1e-10 : factor > 0.1 ? 0.1 : factor;
}
- (NSData *)compressToJPEGFormatDataWithFactor:(CGFloat)factor maxFileSize:(u_int64_t)fileSize
{
if (!self) return nil;
NSData *tempImageData = UIImageJPEGRepresentation(self, 1.0);
if ([tempImageData length] <= fileSize) return tempImageData;
NSData *targetImageData = nil;
CGFloat compressionFactor = clampCompressionFactor(factor);
CGFloat minFactor = 0;
CGFloat maxFactor = 1.0;
CGFloat midFactor = 0;
while (fabs(maxFactor-minFactor) > compressionFactor)
{
@autoreleasepool
{
midFactor = minFactor + (maxFactor - minFactor)/2;
tempImageData = UIImageJPEGRepresentation(self, midFactor);
if ([tempImageData length] > fileSize)
{
maxFactor = midFactor;
}
else
{
minFactor = midFactor;
targetImageData = tempImageData;
}
}
}
return targetImageData;
}
by l.j. twan 2018/11/23
sql
排序返回 , 效率比较低,且依赖数据库设备
redis
的 有序集合 ,直接获取排名
redis
的有序集合 实现依赖于 跳表
+ 散列表
+ ...摘录:
在大规模数据处理环境下,作业效率并不是首要考虑的问题,算法的扩展性和容错性才是首要考虑的。算法应该具有良好的扩展性,以便数据量进一步加大(随着业务的发展,数据量加大是必然的)时,在不修改算法框架的前提下,可达到近似的线性比;算法应该具有容错性,即当前某个文件处理失败后,能自动将其交给另外一个线程继续处理,而不是从头开始处理
by l.j. twan 2018/11/24
类似问题: Word
的拼写检查如何实现 ?
Trie Tree
前缀树具体代码暂时只是一个思路, 自己实现一个 Trie 还是比较难的 😢
by l.j. twan 2018/11/25
摘录:
若要在某一领域内达到专家级的水平,其关键在于“审慎地重复”,也就是说,并非是机械地,一遍又一遍地练习,而是要不断地挑战自我,试图超越自身当前的水平,通过不断的尝试挑战,并在尝试的过程中和尝试之后对自身的表现进行分析和总结,吸取经验,纠正之前犯过的各种错误。把这一“审慎”的过程不断重复,才能取得成功。