CloudKit records saving progress - ios

I want to save Event and Image (for event) records. I want to show uploading progress in UI.
Event is little object with text and location.
Image is big object with photo as UIImage.
Image record is first item in array, event is second.
I create CKModifyRecordsOperation and set two CKRecord objects to recordsToSave
Than I set perRecordProgressBlock:
[operation setPerRecordProgressBlock:^(CKRecord *record, double progress) {
NSLog(#"#record: %# progress: %f", record.recordType, progress);
}];
And perRecordCompletionBlock:
[operation setPerRecordCompletionBlock:^(CKRecord *record, NSError *error) {
NSLog(#"#recordSaved: %# e: %#", record.recordType, error);
}];
I expect to see a lot of rows with progress for two records like:
#record: ImageRecord progress: 0.050000
#record: ImageRecord progress: 0.100000
#record: ImageRecord progress: 0.150000
...
#record: ImageRecord progress: 1.000000
#recordSaved: ImageRecord e: (null)
#record: EventRecord progress: 0.050000
...
#record: EventRecord progress: 1.000000
#recordSaved: EventRecord e: (null)
But really in console output I see:
#record: ImageRecord progress: 0.000000
#record: ImageRecord progress: 0.447357
#record: ImageRecord progress: 1.000000
#record: ImageRecord progress: 1.000000
#recordSaved: Event e: (null)
#recordSaved: ImageRecord e: (null)
What should I do?

The documentation for the CKModifyRecordOperation states:
The operation object executes this block zero or more times for each record in the recordsToSave property.
So if the save action for an object takes little time, then it could happen that you won't get a progress callback. As far as I know there is no way to influence the frequency of progress notifications.

Related

#synchronized the writing queue makes thread safe?

NSObject *token = [[NSObject alloc] init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
while (1) {
#synchronized (token) {
array = [NSArray arrayWithObject:#"1"];
}
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
while (1) {
NSLog(#"%#",array);
}
});
This code won't crash, even there is no #synchronized in the read queue.
As compared,
NSLock *lock = [[NSLock alloc] init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
while (1) {
[lock lock];
array = [NSArray arrayWithObject:#"1"];
[lock unlock];
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
while (1) {
NSLog(#"%#",array);
}
});
This code will crash as array is not thread safe. How does this one-side #synchronized keeps the thread safe?
How does this one-side #synchronized keeps the thread safe?
It doesn’t.
Using #synchronized (or any synchronization method) for writes, but not for reads, is not thread-safe. Just because your code does not readily crash does not mean it is thread-safe. Both reads and writes must be synchronized. Neither of these two examples, with unsynchronized reads, is thread-safe.
If you want to test for thread-safety, consider the thread sanitizer (TSAN). For example, when I ran the #synchronized example through TSAN, it reported:
WARNING: ThreadSanitizer: data race (pid=89608)
Read of size 8 at 0x7b0c0007bce8 by thread T1:
#0 __29-[ViewController viewDidLoad]_block_invoke.14 <null> (MyApp12:x86_64+0x100002e3d)
#1 __tsan::invoke_and_release_block(void*) <null> (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x74d7b)
#2 _dispatch_client_callout <null> (libdispatch.dylib:x86_64+0x40af)
Previous write of size 8 at 0x7b0c0007bce8 by thread T3 (mutexes: write M1679):
#0 __29-[ViewController viewDidLoad]_block_invoke <null> (MyApp12:x86_64+0x100002c02)
#1 __tsan::invoke_and_release_block(void*) <null> (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x74d7b)
#2 _dispatch_client_callout <null> (libdispatch.dylib:x86_64+0x40af)
Location is heap block of size 48 at 0x7b0c0007bcc0 allocated by main thread:
#0 malloc <null> (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x5239a)
#1 _Block_object_assign <null> (libsystem_blocks.dylib:x86_64+0x14fc)
#2 _Block_copy <null> (libsystem_blocks.dylib:x86_64+0x141c)
#3 -[ViewController viewDidLoad] <null> (MyApp12:x86_64+0x100002878)
#4 -[NSViewController _sendViewDidLoad] <null> (AppKit:x86_64+0xb41ce)
#5 start <null> (libdyld.dylib:x86_64+0x15620)
Mutex M1679 (0x7b0400004020) created at:
#0 objc_sync_enter <null> (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x72a45)
#1 __29-[ViewController viewDidLoad]_block_invoke <null> (MyApp12:x86_64+0x100002b51)
#2 __tsan::invoke_and_release_block(void*) <null> (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x74d7b)
#3 _dispatch_client_callout <null> (libdispatch.dylib:x86_64+0x40af)
Thread T1 (tid=3657833, running) is a GCD worker thread
Thread T3 (tid=3657837, running) is a GCD worker thread
SUMMARY: ThreadSanitizer: data race (/Users/.../Library/Developer/Xcode/DerivedData/MyApp-ewpoprpgpjgbmrcrkyycvogiazhl/Build/Products/Debug/MyApp12.app/Contents/MacOS/MyApp:x86_64+0x100002e3d) in __29-[ViewController viewDidLoad]_block_invoke.14+0x6d
Whichever synchronization mechanism you use (whether #synchronized, locks, or GCD), both reads and writes must be synchronized. Data race problems are notoriously difficult to manifest, and, as such, one should hesitate to draw any conclusions from an absence of a crash.
Why does it work? Without going into technical details, and really just guessing, I think this has to do with the way in which the multithreading is implemented.
Reason is based on the following, which will also NOT crash
NSLock * lock = [[NSLock alloc] init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
int i = 0;
while (1) {
lock.lock;
// Do some work
for ( int j = 0; j < 10000; j ++ )
{
double x = atan ( j );
}
array = [NSArray arrayWithObject:#( i )];
lock.unlock;
i ++;
}
});
This is essentially the same as your code, but inside the lock I do some serious work. I think that work is 'hard' enough for the dispatcher to pause the other thread and I think once that work is done, the multithreading dispatcher says enough, time for some other threads, and then this one in turn is put on hold long enough for the other one to finish unmolested.
PS : That said, I think you will get different results as the load on the system changes and you need to properly sync access.

Why "dict[String(aUint16)] = Int(Date().timeIntervalSince1970 * 1000)" failed in some cases? [duplicate]

This question already has an answer here:
Timestamp function that has been working reliably just caused EXC_BAD_INSTRUCTION
(1 answer)
Closed 5 years ago.
My code, which use SimplePing:
func simplePing(_ pinger: SimplePing, didSendPacket packet: Data, sequenceNumber: UInt16) {
begin[String(sequenceNumber)] = Int(Date().timeIntervalSince1970 * 1000)//AppDelegate.swift:185
print("Send: \(Common.startCount)")
}
It works perfectly in my simulator and iPhone, but after made it available on AppStore, I received about 20 crash logs with some error like that:
Exception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x00000000e7ffdefe
Triggered by Thread: 1
...
Thread 1 name:
Thread 1 Crashed:
0 ME 0x0011e5cc specialized AppDelegate.simplePing(SimplePing, didSendPacket : Data, sequenceNumber : UInt16) -> () + 652 (AppDelegate.swift:185)
1 ME 0x00116990 #objc AppDelegate.simplePing(SimplePing, didSendPacket : Data, sequenceNumber : UInt16) -> () + 68
2 ME 0x00116818 #objc AppDelegate.simplePing(SimplePing, didSendPacket : Data, sequenceNumber : UInt16) -> () + 40
3 ME 0x000f3348 -[SimplePing sendPingWithData:] + 640 (SimplePing.m:297)
...
I can't reproduce that crash, so I have to analysis that line of the code:
begin[String(sequenceNumber)]
begin initialized with begin = [String: Int](), so its type is [String : Int], and sequenceNumber's type is UInt16. So I think begin[String(sequenceNumber)] doesn't have any potential bug.
Int(Date().timeIntervalSince1970 * 1000)
And Int(Date().timeIntervalSince1970 * 1000) is just something like Int(aDouble * 1000), it seems correct in any situation.
So I get really confused with that crash log, could anyone please give me some hints?
From the docs for Int:
On 32-bit platforms, Int is the same size as Int32, and on 64-bit platforms, Int is the same size as Int64.
A signed 32-bit integer has a maximum value of 2,147,483,647.
At the moment, Int(Date().timeIntervalSince1970 * 1000) returns a value of 1,495,855,170,970.
That is significantly larger than what will fit in a 32-bit integer.
The crash is being caused by the overflow you are attempting when casting the Double to an Int when run on a 32-bit iOS device.
I would suggest an explicit use of Int64 instead of Int:
begin[String(sequenceNumber)] = Int64(Date().timeIntervalSince1970 * 1000)

Progress on CloudKit/CKAsset upload [duplicate]

I want to save Event and Image (for event) records. I want to show uploading progress in UI.
Event is little object with text and location.
Image is big object with photo as UIImage.
Image record is first item in array, event is second.
I create CKModifyRecordsOperation and set two CKRecord objects to recordsToSave
Than I set perRecordProgressBlock:
[operation setPerRecordProgressBlock:^(CKRecord *record, double progress) {
NSLog(#"#record: %# progress: %f", record.recordType, progress);
}];
And perRecordCompletionBlock:
[operation setPerRecordCompletionBlock:^(CKRecord *record, NSError *error) {
NSLog(#"#recordSaved: %# e: %#", record.recordType, error);
}];
I expect to see a lot of rows with progress for two records like:
#record: ImageRecord progress: 0.050000
#record: ImageRecord progress: 0.100000
#record: ImageRecord progress: 0.150000
...
#record: ImageRecord progress: 1.000000
#recordSaved: ImageRecord e: (null)
#record: EventRecord progress: 0.050000
...
#record: EventRecord progress: 1.000000
#recordSaved: EventRecord e: (null)
But really in console output I see:
#record: ImageRecord progress: 0.000000
#record: ImageRecord progress: 0.447357
#record: ImageRecord progress: 1.000000
#record: ImageRecord progress: 1.000000
#recordSaved: Event e: (null)
#recordSaved: ImageRecord e: (null)
What should I do?
The documentation for the CKModifyRecordOperation states:
The operation object executes this block zero or more times for each record in the recordsToSave property.
So if the save action for an object takes little time, then it could happen that you won't get a progress callback. As far as I know there is no way to influence the frequency of progress notifications.

Make UIProgressView as a count down timer

I've implemented a UIProgressView as a count down timer, its value is decreased from 1.0 to 0.0 in 1.5 seconds.
It already worked but the problem is the time it took was longer than 1.5
seconds, it was about 2.0 - 2.5 seconds until the progress view value reach 0.0
For it to run in just 1.5, I decreased the value of the progress by 0.001 each time it's called. The decrease method is call after 0.0015 second interval.
Below is how I do it. I don't know if there's anything wrong that make it run longer than just 1.5 seconds?
- (void)decreaseProgress {
if (currentProgress > 0.0000f) {
currentProgress -= 0.001f;
[_timeLineProgress setProgress:currentProgress animated:YES];
[self performSelector:#selector(decreaseProgress) withObject:self afterDelay:0.0015 inModes:#[NSDefaultRunLoopMode]];
} else {
[self stopTimer];
}
}
To animate the progress, try this code in your decreaseProgress method
[UIView animateWithDuration:1.5 animations:^{
[_timeLineProgress setProgress:0.0 animated:YES];
}];
You're attempting to update the progress view 1000 times in 1.5 seconds. That's way too fast, since the screen only updates 60 times per second. In other words, you're updating the progress bar more than 10 times between each time that the progress bar is actually redrawn on the screen.
Instead I would suggest 15 updates at 0.1 second intervals, and change the progress bar by 1/15 each time.
One way to check how well the code is performing is to use the CACurrentMediaTime function to get timestamps. Here's some sample code that demonstrates how to do that. The progressStart variable is the timestamp when the button press event occurred, and the NSLog prints the amount of time elapsed relative to the start time.
An important feature of the code is that the performSelector method is called as early as possible in the updateProgress method, to minimize slippage.
#interface ViewController ()
{
CFTimeInterval progressStart;
int progressCount;
}
#property (weak, nonatomic) IBOutlet UIProgressView *progressView;
#end
- (void)updateProgress
{
if ( progressCount > 0 )
[self performSelector:#selector(updateProgress) withObject:nil afterDelay:0.1];
self.progressView.progress = progressCount / 15.0;
NSLog( #"%2d %.3lf", progressCount, CACurrentMediaTime() - progressStart );
progressCount--;
}
- (IBAction)someButtonPressed
{
self.progressView.progress = 1.0;
progressStart = CACurrentMediaTime();
progressCount = 15;
[self updateProgress];
}
And here are the results from a typical run
2015-07-01 13:05:57.610 Progress[8354:907] 15 0.000
2015-07-01 13:05:57.711 Progress[8354:907] 14 0.101
2015-07-01 13:05:57.813 Progress[8354:907] 13 0.203
2015-07-01 13:05:57.914 Progress[8354:907] 12 0.304
2015-07-01 13:05:58.015 Progress[8354:907] 11 0.405
2015-07-01 13:05:58.116 Progress[8354:907] 10 0.506
2015-07-01 13:05:58.218 Progress[8354:907] 9 0.608
2015-07-01 13:05:58.319 Progress[8354:907] 8 0.709
2015-07-01 13:05:58.420 Progress[8354:907] 7 0.810
2015-07-01 13:05:58.520 Progress[8354:907] 6 0.910
2015-07-01 13:05:58.621 Progress[8354:907] 5 1.011
2015-07-01 13:05:58.722 Progress[8354:907] 4 1.112
2015-07-01 13:05:58.823 Progress[8354:907] 3 1.213
2015-07-01 13:05:58.924 Progress[8354:907] 2 1.314
2015-07-01 13:05:59.024 Progress[8354:907] 1 1.415
2015-07-01 13:05:59.125 Progress[8354:907] 0 1.515
Note that the performSelector:afterDelay method has about 1 millisecond of slippage on each event. The total slippage was 15 milliseconds. The device screen update rate is 60 frames/sec, which is 16.7 msec/frame. So the total slippage was less than one frame time, and won't be noticeable to the user.
As rmaddy pointed out in the comments, using an NSTimer allows you to avoid most of the slippage. However, the last timer event could still slip by an arbitrary amount of time.

Buffering Problems in Avqueueplayer

I have a list of urls to be played from an avqueueplayer . I am loading the tracks and playable keys and adding the AVplayeritems into the Queue player one by one after each Item has finished playing.
Iam handling the buffering states of the player using the following code
for (NSValue *time in player.currentItem.loadedTimeRanges)
{
CMTimeRange range;
[time getValue:&range];
CGFloat seekTimeInSeconds = [self playerTimeToSeek];
CMTime timeSeek = CMTimeMakeWithSeconds(seekTimeInSeconds, NSEC_PER_SEC);
bufferReady = CMTimeRangeContainsTime(range, timeSeek);
NSLog(#"%f , %f ",CMTimeGetSeconds(range.start), CMTimeGetSeconds(range.duration));
if (CMTimeGetSeconds(range.duration)==0)
{
[self.player seekToTime: CMTimeMakeWithSeconds(seekTimeInSeconds, NSEC_PER_SEC) ];
}
if ((seekTimeInSeconds) >= CMTimeGetSeconds(self.player.currentItem.duration)) //
{
bufferReady = YES;
}
if (bufferReady)
{
//if (self.bufferEmptyOccured || change|| self.bufferNeedsUpdate)
{
[self manageBufferSufficientState];
self.bufferNeedsUpdate = NO;
}
self.bufferEmptyOccured = NO;
break;
}
}
if (!player.currentItem && self.nowPlayingAudioRecord[#"AudioItem"])
{
{
bufferReady = YES;
//if (self.bufferEmptyOccured||change || self.bufferNeedsUpdate)
{
[self manageBufferSufficientState];
self.bufferNeedsUpdate = NO;
}
self.bufferEmptyOccured = NO;
}
}
if (self.nowPlayingAudioRecord &&! self.nowPlayingAudioRecord[#"AudioItem"])
{
bufferReady = NO;
}
if (!bufferReady)
{
//if (!self.bufferEmptyOccured || change || self.bufferNeedsUpdate)
{
[self manageBufferEmptyState];
if (self.bufferNeedsUpdate)
{
self.bufferNeedsUpdate = NO;
}
}
self.bufferEmptyOccured = YES;
}
});
and 'playertimetoseek' method determines the time to which the player has to seek in case buffer is ready. This is because each of my urls need to be played at a predefined time to be played.
This method is called regularly once every 0.6 secs by using a timer to verify the buffering status
It works well normally , but for some reason , a few urls never get buffered ( each audio is of 10-20 sec duration) within the duration of the audio and the log gets printed as
2013-11-21 18:51:02.404 myapp[523:1000b] 1.009063 , 0.000000
2013-11-21 18:51:03.003 myapp[523:1000b] 1.608808 , 0.000000
2013-11-21 18:51:03.606 myapp[523:1000b] 2.208690 , 0.000000
2013-11-21 18:51:04.203 myapp[523:1000b] 2.811525 , 0.000000
2013-11-21 18:51:04.804 myapp[523:1000b] 3.408591 , 0.000000
2013-11-21 18:51:05.403 myapp[523:1000b] 4.009229 , 0.000000
2013-11-21 18:51:06.003 myapp[523:1000b] 4.608602 , 0.000000
2013-11-21 18:51:06.604 myapp[523:1000b] 5.208743 , 0.000000
2013-11-21 18:51:07.204 myapp[523:1000b] 5.809145 , 0.000000
2013-11-21 18:51:07.803 myapp[523:1000b] 6.409183 , 0.000000
2013-11-21 18:51:08.404 myapp[523:1000b] 7.008626 , 0.000000
2013-11-21 18:51:09.003 myapp[523:1000b] 7.609262 , 0.000000
2013-11-21 18:51:09.604 myapp[523:1000b] 8.208768 , 0.000000
2013-11-21 18:51:10.203 myapp[523:1000b] 8.809213 , 0.000000
2013-11-21 18:51:10.804 myapp[523:1000b] 9.408639 , 0.000001
2013-11-21 18:51:11.404 myapp[523:1000b] 9.408639 , 0.000001
2013-11-21 18:51:12.007 myapp[523:1000b] 10.579592 , 0.000000
Note that I am on a decent network and the audio bitrate is 128Kbps. Why could this be happening ? Is there any specific reason for this ?
EDIT:: In a few other cases the logs are as below :
2013-11-21 20:53:33.970 myApp[819:605b] 2.358489 , 0.000000
2013-11-21 20:53:34.570 myApp[819:605b] 2.958184 , 0.000000
2013-11-21 20:53:35.170 myApp[819:605b] 3.558097 , 0.000000
2013-11-21 20:53:35.770 myApp[819:605b] 4.158253 , 0.000000
2013-11-21 20:53:36.371 myApp[819:605b] 4.758271 , 0.000000
2013-11-21 20:53:36.970 myApp[819:605b] 5.358620 , 0.000000
2013-11-21 20:53:37.570 myApp[819:605b] 5.958164 , 0.000000
2013-11-21 20:53:38.170 myApp[819:605b] 6.558318 , 0.000000
2013-11-21 20:53:38.770 myApp[819:605b] 7.158033 , 0.000000
2013-11-21 20:53:39.371 myApp[819:605b] 7.758199 , 0.000009
2013-11-21 20:53:39.971 myApp[819:605b] 7.758199 , 0.417969
2013-11-21 20:53:40.571 myApp[819:605b] 7.758199 , 9.247356
2013-11-21 20:53:41.171 myApp[819:605b] 9.978836 , 19.095449
2013-11-21 20:53:41.770 myApp[819:605b] 9.978836 , 19.095449
2013-11-21 20:53:42.371 myApp[819:605b] 9.978836 , 19.095449
2013-11-21 20:53:42.971 myApp[819:605b] 9.978836 , 19.095449
2013-11-21 20:53:43.573 myApp[819:605b] 9.978836 , 19.095449
2013-11-21 20:53:44.171 myApp[819:605b] 9.978836 , 19.095449
How does this happen . It hasn't buffered anything until 20:53:39.971 and buffered almost everything within 0.6 seconds . How exactly does buffering work ?

Resources